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