CXXR (C++ R)
gzio.h
1 /*
2  Based on gzio.c from zlib 1.2.3, but considerably modified!
3 
4  Copyright (C) 1995-2005 Jean-loup Gailly.
5  For conditions of distribution and use, see copyright notice in zlib.h:
6 
7  This software is provided 'as-is', without any express or implied
8  warranty. In no event will the authors be held liable for any damages
9  arising from the use of this software.
10 
11  Permission is granted to anyone to use this software for any purpose,
12  including commercial applications, and to alter it and redistribute it
13  freely, subject to the following restrictions:
14 
15  1. The origin of this software must not be misrepresented; you must not
16  claim that you wrote the original software. If you use this software
17  in a product, an acknowledgment in the product documentation would be
18  appreciated but is not required.
19  2. Altered source versions must be plainly marked as such, and must not be
20  misrepresented as being the original software.
21  3. This notice may not be removed or altered from any source distribution.
22 
23  Jean-loup Gailly Mark Adler
24  jloup@gzip.org madler@alumni.caltech.edu
25 
26 */
27 
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h> /* for Win32, HAVE_OFF_T and HAVE_FSEEKO */
31 #endif
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 
38 #include "zlib.h"
39 
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43 
44 #ifdef Win32
45 # define OS_CODE 0x06
46 #else
47 # define OS_CODE 0x03
48 #endif
49 
50 /* R ADDITION */
51 #ifdef Win32
52 # define Rz_off_t off64_t
53 #elif defined(HAVE_OFF_T) && defined(HAVE_FSEEKO)
54 # define Rz_off_t off_t
55 #else
56 # define Rz_off_t long
57 #endif
58 
59 
60 #define Z_BUFSIZE 16384
61 
62 typedef struct gz_stream {
63  z_stream stream;
64  int z_err; /* error code for last stream operation */
65  int z_eof; /* set if end of input file */
66  FILE *file; /* .gz file */
67  Byte buffer[Z_BUFSIZE]; /* input or output buffer */
68  uLong crc; /* crc32 of uncompressed data */
69  int transparent; /* 1 if input file is not compressed */
70  char mode; /* 'w' or 'r' */
71  Rz_off_t start; /* start of compressed data in file (header skipped) */
72  Rz_off_t in; /* bytes into deflate or inflate */
73  Rz_off_t out; /* bytes out of deflate or inflate */
74 } gz_stream;
75 
76 
77 static int get_byte(gz_stream *s)
78 {
79  if (s->z_eof) return EOF;
80  if (s->stream.avail_in == 0) {
81  errno = 0;
82  s->stream.avail_in = (uInt) fread(s->buffer, 1, Z_BUFSIZE, s->file);
83  if (s->stream.avail_in == 0) {
84  s->z_eof = 1;
85  if (ferror(s->file)) s->z_err = Z_ERRNO;
86  return EOF;
87  }
88  s->stream.next_in = s->buffer;
89  }
90  s->stream.avail_in--;
91  return *(s->stream.next_in)++;
92 }
93 
94 static int destroy (gz_stream *s)
95 {
96  int err = Z_OK;
97 
98  if (!s) return Z_STREAM_ERROR;
99 
100  if (s->stream.state != NULL) {
101  if (s->mode == 'w') err = deflateEnd(&(s->stream));
102  else if (s->mode == 'r') err = inflateEnd(&(s->stream));
103  }
104  if (s->file != NULL && fclose(s->file)) {
105 #ifdef ESPIPE
106  if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
107 #endif
108  err = Z_ERRNO;
109  }
110  if (s->z_err < 0) err = s->z_err;
111 
112  if(s) free(s);
113  return err;
114 }
115 
116 static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
117 
118 /* gzip flag byte */
119 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text, unused */
120 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
121 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
122 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
123 #define COMMENT 0x10 /* bit 4 set: file comment present */
124 #define RESERVED 0xE0 /* bits 5..7: reserved */
125 
126 static void check_header(gz_stream *s)
127 {
128  int method; /* method byte */
129  int flags; /* flags byte */
130  uInt len;
131  int c;
132 
133  /* Assure two bytes in the buffer so we can peek ahead -- handle case
134  where first byte of header is at the end of the buffer after the last
135  gzip segment */
136  len = s->stream.avail_in;
137  if (len < 2) {
138  if (len) s->buffer[0] = s->stream.next_in[0];
139  errno = 0;
140  len = (uInt) fread(s->buffer + len, 1, Z_BUFSIZE >> len, s->file);
141  if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
142  s->stream.avail_in += len;
143  s->stream.next_in = s->buffer;
144  if (s->stream.avail_in < 2) {
145  s->transparent = s->stream.avail_in;
146  return;
147  }
148  }
149 
150  /* Peek ahead to check the gzip magic header */
151  if (s->stream.next_in[0] != gz_magic[0] ||
152  s->stream.next_in[1] != gz_magic[1]) {
153  s->transparent = 1;
154  return;
155  }
156  s->stream.avail_in -= 2;
157  s->stream.next_in += 2;
158 
159  /* Check the rest of the gzip header */
160  method = get_byte(s);
161  flags = get_byte(s);
162  if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
163  s->z_err = Z_DATA_ERROR;
164  return;
165  }
166 
167  /* Discard time, xflags and OS code: */
168  for (len = 0; len < 6; len++) (void)get_byte(s);
169 
170  if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
171  len = (uInt )get_byte(s);
172  len += ((uInt) get_byte(s)) << 8;
173  /* len is garbage if EOF but the loop below will quit anyway */
174  while (len-- != 0 && get_byte(s) != EOF) ;
175  }
176  if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
177  while ((c = get_byte(s)) != 0 && c != EOF) ;
178  }
179  if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
180  while ((c = get_byte(s)) != 0 && c != EOF) ;
181  }
182  if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
183  for (len = 0; len < 2; len++) (void) get_byte(s);
184  }
185  s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
186 }
187 
188 gzFile R_gzopen (const char *path, const char *mode)
189 {
190  int err;
191  int level = Z_DEFAULT_COMPRESSION; /* compression level */
192  int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
193  char *p = (char *) mode;
194  gz_stream *s;
195  char fmode[80]; /* copy of mode, without the compression level */
196  char *m = fmode;
197 
198  if (!path || !mode) return Z_NULL;
199 
200  s = (gz_stream *) malloc(sizeof(gz_stream));
201  if (!s) return Z_NULL;
202 
203  s->stream.zalloc = (alloc_func) 0;
204  s->stream.zfree = (free_func) 0;
205  s->stream.opaque = (voidpf) 0;
206  s->stream.next_in = s->buffer;
207  s->stream.next_out = s->buffer;
208  s->stream.avail_in = s->stream.avail_out = 0;
209  s->file = NULL;
210  s->z_err = Z_OK;
211  s->z_eof = 0;
212  s->in = 0;
213  s->out = 0;
214  s->crc = crc32(0L, Z_NULL, 0);
215  s->transparent = 0;
216  s->mode = '\0';
217  do {
218  if (*p == 'r') s->mode = 'r';
219  if (*p == 'w' || *p == 'a') s->mode = 'w';
220  if (*p >= '0' && *p <= '9') level = *p - '0';
221  else if (*p == 'f') strategy = Z_FILTERED;
222  else if (*p == 'h') strategy = Z_HUFFMAN_ONLY;
223  else if (*p == 'R') strategy = Z_RLE;
224  else *m++ = *p; /* copy the mode */
225  } while (*p++ && m != fmode + sizeof(fmode));
226  if (s->mode == '\0') return destroy(s), (gzFile) Z_NULL;
227 
228  if (s->mode == 'w') {
229  err = deflateInit2(&(s->stream), level,
230  Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, strategy);
231  /* windowBits is passed < 0 to suppress zlib header */
232  if (err != Z_OK) return destroy(s), (gzFile) Z_NULL;
233  } else {
234  err = inflateInit2(&(s->stream), -MAX_WBITS);
235  /* windowBits is passed < 0 to tell that there is no zlib header.
236  * Note that in this case inflate *requires* an extra "dummy" byte
237  * after the compressed stream in order to complete decompression and
238  * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
239  * present after the compressed stream.
240  */
241  if (err != Z_OK) return destroy(s), (gzFile) Z_NULL;
242  }
243  s->stream.avail_out = Z_BUFSIZE;
244 
245  errno = 0;
246  s->file = fopen(path, fmode);
247  if (s->file == NULL) return destroy(s), (gzFile) Z_NULL;
248 
249  if (s->mode == 'w') {
250  /* Write a very simple .gz header */
251  fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
252  Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
253  OS_CODE);
254  s->start = 10L;
255  } else {
256  check_header(s); /* skip the .gz header */
257  s->start = f_tell(s->file) - s->stream.avail_in;
258  }
259  return (gzFile) s;
260 }
261 
262 static void z_putLong (FILE *file, uLong x)
263 {
264  int n;
265  for (n = 0; n < 4; n++) {
266  fputc((int) (x & 0xff), file);
267  x >>= 8;
268  }
269 }
270 
271 static uLong getLong (gz_stream *s)
272 {
273  uLong x = (uLong) get_byte(s);
274  int c;
275 
276  x += ((uLong) get_byte(s)) << 8;
277  x += ((uLong) get_byte(s)) << 16;
278  c = get_byte(s);
279  if (c == EOF) s->z_err = Z_DATA_ERROR;
280  x += ((uLong) c) << 24;
281  return x;
282 }
283 
284 static int R_gzread (gzFile file, voidp buf, unsigned len)
285 {
286  gz_stream *s = (gz_stream*) file;
287  Bytef *start = (Bytef*) buf; /* starting point for crc computation */
288  Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
289 
290  if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
291 
292  if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
293  if (s->z_err == Z_STREAM_END) return 0; /* EOF */
294 
295  next_out = (Byte*) buf;
296  s->stream.next_out = (Bytef*) buf;
297  s->stream.avail_out = len;
298 
299  while (s->stream.avail_out != 0) {
300 
301  if (s->transparent) {
302  /* Copy first the lookahead bytes: */
303  uInt n = s->stream.avail_in;
304  if (n > s->stream.avail_out) n = s->stream.avail_out;
305  if (n > 0) {
306  memcpy(s->stream.next_out, s->stream.next_in, n);
307  next_out += n;
308  s->stream.next_out = next_out;
309  s->stream.next_in += n;
310  s->stream.avail_out -= n;
311  s->stream.avail_in -= n;
312  }
313  if (s->stream.avail_out > 0) {
314  s->stream.avail_out -=
315  (uInt) fread(next_out, 1, s->stream.avail_out, s->file);
316  }
317  len -= s->stream.avail_out;
318  s->in += len;
319  s->out += len;
320  if (len == 0) s->z_eof = 1;
321  return (int)len;
322  }
323  if (s->stream.avail_in == 0 && !s->z_eof) {
324  errno = 0;
325  s->stream.avail_in = (uInt) fread(s->buffer, 1, Z_BUFSIZE, s->file);
326  if (s->stream.avail_in == 0) {
327  s->z_eof = 1;
328  if (ferror(s->file)) {
329  s->z_err = Z_ERRNO;
330  break;
331  }
332  }
333  s->stream.next_in = s->buffer;
334  }
335  s->in += s->stream.avail_in;
336  s->out += s->stream.avail_out;
337  s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
338  s->in -= s->stream.avail_in;
339  s->out -= s->stream.avail_out;
340 
341  if (s->z_err == Z_STREAM_END) {
342  /* Check CRC and original size */
343  s->crc = crc32(s->crc, start, (uInt) (s->stream.next_out - start));
344  start = s->stream.next_out;
345 
346  if (getLong(s) != s->crc) {
347  s->z_err = Z_DATA_ERROR;
348  } else {
349  (void)getLong(s);
350  /* The uncompressed length returned by above getlong() may be
351  * different from s->out in case of concatenated .gz files.
352  * Check for such files:
353  */
354  check_header(s);
355  if (s->z_err == Z_OK) {
356  inflateReset(&(s->stream));
357  s->crc = crc32(0L, Z_NULL, 0);
358  }
359  }
360  }
361  if (s->z_err != Z_OK || s->z_eof) break;
362  }
363  s->crc = crc32(s->crc, start, (uInt) (s->stream.next_out - start));
364 
365  if (len == s->stream.avail_out &&
366  (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO))
367  return -1;
368  return (int)(len - s->stream.avail_out);
369 }
370 
371 /* for devPS.c */
372 char *R_gzgets(gzFile file, char *buf, int len)
373 {
374  char *b = buf;
375  if (buf == Z_NULL || len <= 0) return Z_NULL;
376 
377  while (--len > 0 && R_gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
378  *buf = '\0';
379  return b == buf && len > 0 ? Z_NULL : b;
380 }
381 
382 
383 static int R_gzwrite (gzFile file, voidpc buf, unsigned len)
384 {
385  gz_stream *s = (gz_stream*) file;
386 
387  if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
388 
389  s->stream.next_in = (Bytef*) buf;
390  s->stream.avail_in = len;
391 
392  while (s->stream.avail_in != 0) {
393  if (s->stream.avail_out == 0) {
394  s->stream.next_out = s->buffer;
395  if (fwrite(s->buffer, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
396  s->z_err = Z_ERRNO;
397  break;
398  }
399  s->stream.avail_out = Z_BUFSIZE;
400  }
401  s->in += s->stream.avail_in;
402  s->out += s->stream.avail_out;
403  s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
404  s->in -= s->stream.avail_in;
405  s->out -= s->stream.avail_out;
406  if (s->z_err != Z_OK) break;
407  }
408  s->crc = crc32(s->crc, (const Bytef *) buf, len);
409 
410  return (int) (len - s->stream.avail_in);
411 }
412 
413 
414 static int gz_flush (gzFile file, int flush)
415 {
416  uInt len;
417  int done = 0;
418  gz_stream *s = (gz_stream*) file;
419 
420  if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
421 
422  s->stream.avail_in = 0; /* should be zero already anyway */
423 
424  for (;;) {
425  len = Z_BUFSIZE - s->stream.avail_out;
426  if (len != 0) {
427  if ((uInt)fwrite(s->buffer, 1, len, s->file) != len) {
428  s->z_err = Z_ERRNO;
429  return Z_ERRNO;
430  }
431  s->stream.next_out = s->buffer;
432  s->stream.avail_out = Z_BUFSIZE;
433  }
434  if (done) break;
435  s->out += s->stream.avail_out;
436  s->z_err = deflate(&(s->stream), flush);
437  s->out -= s->stream.avail_out;
438 
439  /* Ignore the second of two consecutive flushes: */
440  if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
441 
442  /* deflate has finished flushing only when it hasn't used up
443  * all the available space in the output buffer:
444  */
445  done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
446 
447  if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
448  }
449  return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
450 }
451 
452 /* return value 0 for success, 1 for failure */
453 static int int_gzrewind (gzFile file)
454 {
455  gz_stream *s = (gz_stream*) file;
456 
457  if (s == NULL || s->mode != 'r') return -1;
458 
459  s->z_err = Z_OK;
460  s->z_eof = 0;
461  s->stream.avail_in = 0;
462  s->stream.next_in = s->buffer;
463  s->crc = crc32(0L, Z_NULL, 0);
464  if (!s->transparent) (void) inflateReset(&s->stream);
465  s->in = 0;
466  s->out = 0;
467  return f_seek(s->file, s->start, SEEK_SET);
468 }
469 
470 static Rz_off_t R_gztell (gzFile file)
471 {
472  gz_stream *s = (gz_stream*) file;
473  if (s->mode == 'w') return s->in; else return s->out;
474 }
475 
476 /* NB: return value is in line with fseeko, not gzseek */
477 static int R_gzseek (gzFile file, Rz_off_t offset, int whence)
478 {
479  gz_stream *s = (gz_stream*) file;
480 
481  if (s == NULL || whence == SEEK_END ||
482  s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) return -1;
483 
484  if (s->mode == 'w') {
485  if (whence == SEEK_SET) offset -= s->in;
486  if (offset < 0) return -1;
487 
488  /* At this point, offset is the number of zero bytes to write. */
489  memset(s->buffer, 0, Z_BUFSIZE);
490  while (offset > 0) {
491  uInt size = Z_BUFSIZE;
492  if (offset < Z_BUFSIZE) size = (uInt) offset;
493  size = R_gzwrite(file, s->buffer, size);
494  if (size == 0) return -1;
495  offset -= size;
496  }
497  return 0;
498  }
499 
500  /* Rest of function is for reading only */
501 
502  /* compute absolute position */
503  if (whence == SEEK_CUR) offset += s->out;
504  if (offset < 0) return -1;
505 
506  if (s->transparent) {
507  s->stream.avail_in = 0;
508  s->stream.next_in = s->buffer;
509  if (f_seek(s->file, offset, SEEK_SET) < 0) return -1;
510  s->in = s->out = offset;
511  return 0;
512  }
513 
514  /* For a negative seek, rewind and use positive seek */
515  if (offset >= s->out) offset -= s->out;
516  else if (int_gzrewind(file) < 0) return -1;
517 
518  /* offset is now the number of bytes to skip. */
519  while (offset > 0) {
520  int size = Z_BUFSIZE;
521  if (offset < Z_BUFSIZE) size = (int) offset;
522  size = R_gzread(file, s->buffer, (uInt) size);
523  if (size <= 0) return -1;
524  offset -= size;
525  }
526  return 0;
527 }
528 
529 int R_gzclose (gzFile file)
530 {
531  gz_stream *s = (gz_stream*) file;
532  if (s == NULL) return Z_STREAM_ERROR;
533  if (s->mode == 'w') {
534  if (gz_flush (file, Z_FINISH) != Z_OK)
535  return destroy((gz_stream*) file);
536  z_putLong (s->file, s->crc);
537  z_putLong (s->file, (uLong) (s->in & 0xffffffff));
538  }
539  return destroy((gz_stream*) file);
540 }
541 
542 #ifdef __cplusplus
543 }
544 #endif