1/* lib/font/fontfile/gunzip.c
2   written by Mark Eichin <eichin@kitten.gen.ma.us> September 1996.
3   intended for inclusion in X11 public releases. */
4
5#ifdef HAVE_CONFIG_H
6#include <config.h>
7#endif
8#include "libxfontint.h"
9#include <X11/fonts/fontmisc.h>
10#include <X11/fonts/bufio.h>
11#include <zlib.h>
12
13typedef struct _xzip_buf {
14  z_stream z;
15  int zstat;
16  BufChar b[BUFFILESIZE];
17  BufChar b_in[BUFFILESIZE];
18  BufFilePtr f;
19} xzip_buf;
20
21static int BufZipFileClose ( BufFilePtr f, int flag );
22static int BufZipFileFill ( BufFilePtr f );
23static int BufZipFileSkip ( BufFilePtr f, int c );
24static int BufCheckZipHeader ( BufFilePtr f );
25
26BufFilePtr
27BufFilePushZIP (BufFilePtr f)
28{
29  xzip_buf *x;
30
31  x = malloc (sizeof (xzip_buf));
32  if (!x) return 0;
33  /* these are just for raw calloc/free */
34  x->z.zalloc = Z_NULL;
35  x->z.zfree = Z_NULL;
36  x->z.opaque = Z_NULL;
37  x->f = f;
38
39  /* force inflateInit to allocate it's own history buffer */
40  x->z.next_in = Z_NULL;
41  x->z.next_out = Z_NULL;
42  x->z.avail_in = x->z.avail_out = 0;
43
44  /* using negative windowBits sets "nowrap" mode, which turns off
45     zlib header checking [undocumented, for gzip compatibility only?] */
46  x->zstat = inflateInit2(&(x->z), -MAX_WBITS);
47  if (x->zstat != Z_OK) {
48    free(x);
49    return 0;
50  }
51
52  /* now that the history buffer is allocated, we provide the data buffer */
53  x->z.next_out = x->b;
54  x->z.avail_out = BUFFILESIZE;
55  x->z.next_out = x->b_in;
56  x->z.avail_in = 0;
57
58  if (BufCheckZipHeader(x->f)) {
59    free(x);
60    return 0;
61  }
62
63  return BufFileCreate((char *)x,
64		       BufZipFileFill,
65		       0,
66		       BufZipFileSkip,
67		       BufZipFileClose);
68}
69
70static int
71BufZipFileClose(BufFilePtr f, int flag)
72{
73  xzip_buf *x = (xzip_buf *)f->private;
74  inflateEnd (&(x->z));
75  BufFileClose (x->f, flag);
76  free (x);
77  return 1;
78}
79
80/* here's the real work.
81   -- we need to put stuff in f.buffer, update f.left and f.bufp,
82      then return the first byte (or BUFFILEEOF).
83   -- to do this, we need to get stuff into avail_in, and next_in,
84      and call inflate appropriately.
85   -- we may also need to add CRC maintenance - if inflate tells us
86      Z_STREAM_END, we then have 4bytes CRC and 4bytes length...
87   gzio.c:gzread shows most of the mechanism.
88   */
89static int
90BufZipFileFill (BufFilePtr f)
91{
92  xzip_buf *x = (xzip_buf *)f->private;
93
94  /* we only get called when left == 0... */
95  /* but just in case, deal */
96  if (f->left >= 0) {
97    f->left--;
98    return *(f->bufp++);
99  }
100  /* did we run out last time? */
101  switch (x->zstat) {
102  case Z_OK:
103    break;
104  case Z_STREAM_END:
105  case Z_DATA_ERROR:
106  case Z_ERRNO:
107      f->left = 0;
108      return BUFFILEEOF;
109  default:
110    return BUFFILEEOF;
111  }
112  /* now we work to consume what we can */
113  /* let zlib know what we can handle */
114  x->z.next_out = x->b;
115  x->z.avail_out = BUFFILESIZE;
116
117  /* and try to consume all of it */
118  while (x->z.avail_out > 0) {
119    /* if we don't have anything to work from... */
120    if (x->z.avail_in == 0) {
121      /* ... fill the z buf from underlying file */
122      int i, c;
123      for (i = 0; i < sizeof(x->b_in); i++) {
124	c = BufFileGet(x->f);
125	if (c == BUFFILEEOF) break;
126	x->b_in[i] = c;
127      }
128      x->z.avail_in += i;
129      x->z.next_in = x->b_in;
130    }
131    /* so now we have some output space and some input data */
132    x->zstat = inflate(&(x->z), Z_NO_FLUSH);
133    /* the inflation output happens in the f buffer directly... */
134    if (x->zstat == Z_STREAM_END) {
135      /* deal with EOF, crc */
136      break;
137    }
138    if (x->zstat != Z_OK) {
139      break;
140    }
141  }
142  f->bufp = x->b;
143  f->left = BUFFILESIZE - x->z.avail_out;
144
145  if (f->left >= 0) {
146    f->left--;
147    return *(f->bufp++);
148  } else {
149    return BUFFILEEOF;
150  }
151}
152
153/* there should be a BufCommonSkip... */
154static int
155BufZipFileSkip (BufFilePtr f, int c)
156{
157  /* BufFileRawSkip returns the count unchanged.
158     BufCompressedSkip returns 0.
159     That means it probably never gets called... */
160  int retval = c;
161  while(c--) {
162    int get = BufFileGet(f);
163    if (get == BUFFILEEOF) return get;
164  }
165  return retval;
166}
167
168/* now we need to duplicate check_header */
169/* contents:
170     0x1f, 0x8b	-- magic number
171     1 byte	-- method (Z_DEFLATED)
172     1 byte	-- flags (mask with RESERVED -> fail)
173     4 byte	-- time (discard)
174     1 byte	-- xflags (discard)
175     1 byte	-- "os" code (discard)
176     [if flags & EXTRA_FIELD:
177         2 bytes -- LSBfirst length n
178	 n bytes -- extra data (discard)]
179     [if flags & ORIG_NAME:
180	 n bytes -- null terminated name (discard)]
181     [if flags & COMMENT:
182	 n bytes -- null terminated comment (discard)]
183     [if flags & HEAD_CRC:
184         2 bytes -- crc of headers? (discard)]
185 */
186
187/* gzip flag byte -- from gzio.c */
188#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
189#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
190#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
191#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
192#define COMMENT      0x10 /* bit 4 set: file comment present */
193#define RESERVED     0xE0 /* bits 5..7: reserved */
194
195#define GET(f) do {c = BufFileGet(f); if (c == BUFFILEEOF) return c;} while(0)
196static int
197BufCheckZipHeader(BufFilePtr f)
198{
199  int c, flags;
200  GET(f); if (c != 0x1f) return 1; /* magic 1 */
201  GET(f); if (c != 0x8b) return 2; /* magic 2 */
202  GET(f); if (c != Z_DEFLATED) return 3; /* method */
203  GET(f); if (c & RESERVED) return 4; /* reserved flags */
204  flags = c;
205  GET(f); GET(f); GET(f); GET(f); /* time */
206  GET(f);			/* xflags */
207  GET(f);			/* os code */
208  if (flags & EXTRA_FIELD) {
209    int len;
210    GET(f); len = c;
211    GET(f); len += (c<<8);
212    while (len-- >= 0) {
213      GET(f);
214    }
215  }
216  if (flags & ORIG_NAME) {
217    do { GET(f); } while (c != 0);
218  }
219  if (flags & COMMENT) {
220    do { GET(f); } while (c != 0);
221  }
222  if (flags & HEAD_CRC) {
223    GET(f); GET(f);		/* header crc */
224  }
225  return 0;
226}
227