gzlib.c revision 237410
1/* gzlib.c -- zlib functions common to reading and writing gzip files
2 * Copyright (C) 2004, 2010, 2011, 2012 Mark Adler
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 */
5
6/* $FreeBSD: head/lib/libz/gzlib.c 237410 2012-06-21 21:47:08Z delphij $ */
7
8#include "gzguts.h"
9#include "zutil.h"
10
11#if defined(_WIN32) && !defined(__BORLANDC__)
12#  define LSEEK _lseeki64
13#else
14#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
15#  define LSEEK lseek64
16#else
17#  define LSEEK lseek
18#endif
19#endif
20
21/* Local functions */
22local void gz_reset OF((gz_statep));
23local gzFile gz_open OF((const void *, int, const char *));
24
25#if defined UNDER_CE
26
27/* Map the Windows error number in ERROR to a locale-dependent error message
28   string and return a pointer to it.  Typically, the values for ERROR come
29   from GetLastError.
30
31   The string pointed to shall not be modified by the application, but may be
32   overwritten by a subsequent call to gz_strwinerror
33
34   The gz_strwinerror function does not change the current setting of
35   GetLastError. */
36char ZLIB_INTERNAL *gz_strwinerror (error)
37     DWORD error;
38{
39    static char buf[1024];
40
41    wchar_t *msgbuf;
42    DWORD lasterr = GetLastError();
43    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
44        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
45        NULL,
46        error,
47        0, /* Default language */
48        (LPVOID)&msgbuf,
49        0,
50        NULL);
51    if (chars != 0) {
52        /* If there is an \r\n appended, zap it.  */
53        if (chars >= 2
54            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
55            chars -= 2;
56            msgbuf[chars] = 0;
57        }
58
59        if (chars > sizeof (buf) - 1) {
60            chars = sizeof (buf) - 1;
61            msgbuf[chars] = 0;
62        }
63
64        wcstombs(buf, msgbuf, chars + 1);
65        LocalFree(msgbuf);
66    }
67    else {
68        sprintf(buf, "unknown win32 error (%ld)", error);
69    }
70
71    SetLastError(lasterr);
72    return buf;
73}
74
75#endif /* UNDER_CE */
76
77/* Reset gzip file state */
78local void gz_reset(state)
79    gz_statep state;
80{
81    state->x.have = 0;              /* no output data available */
82    if (state->mode == GZ_READ) {   /* for reading ... */
83        state->eof = 0;             /* not at end of file */
84        state->past = 0;            /* have not read past end yet */
85        state->how = LOOK;          /* look for gzip header */
86    }
87    state->seek = 0;                /* no seek request pending */
88    gz_error(state, Z_OK, NULL);    /* clear error */
89    state->x.pos = 0;               /* no uncompressed data yet */
90    state->strm.avail_in = 0;       /* no input data yet */
91}
92
93/* Open a gzip file either by name or file descriptor. */
94local gzFile gz_open(path, fd, mode)
95    const void *path;
96    int fd;
97    const char *mode;
98{
99    gz_statep state;
100    size_t len;
101    int oflag;
102#ifdef O_CLOEXEC
103    int cloexec = 0;
104#endif
105#ifdef O_EXCL
106    int exclusive = 0;
107#endif
108
109    /* check input */
110    if (path == NULL)
111        return NULL;
112
113    /* allocate gzFile structure to return */
114    state = malloc(sizeof(gz_state));
115    if (state == NULL)
116        return NULL;
117    state->size = 0;            /* no buffers allocated yet */
118    state->want = GZBUFSIZE;    /* requested buffer size */
119    state->msg = NULL;          /* no error message yet */
120
121    /* interpret mode */
122    state->mode = GZ_NONE;
123    state->level = Z_DEFAULT_COMPRESSION;
124    state->strategy = Z_DEFAULT_STRATEGY;
125    state->direct = 0;
126    while (*mode) {
127        if (*mode >= '0' && *mode <= '9')
128            state->level = *mode - '0';
129        else
130            switch (*mode) {
131            case 'r':
132                state->mode = GZ_READ;
133                break;
134#ifndef NO_GZCOMPRESS
135            case 'w':
136                state->mode = GZ_WRITE;
137                break;
138            case 'a':
139                state->mode = GZ_APPEND;
140                break;
141#endif
142            case '+':       /* can't read and write at the same time */
143                free(state);
144                return NULL;
145            case 'b':       /* ignore -- will request binary anyway */
146                break;
147#ifdef O_CLOEXEC
148            case 'e':
149                cloexec = 1;
150                break;
151#endif
152#ifdef O_EXCL
153            case 'x':
154                exclusive = 1;
155                break;
156#endif
157            case 'f':
158                state->strategy = Z_FILTERED;
159                break;
160            case 'h':
161                state->strategy = Z_HUFFMAN_ONLY;
162                break;
163            case 'R':
164                state->strategy = Z_RLE;
165                break;
166            case 'F':
167                state->strategy = Z_FIXED;
168            case 'T':
169                state->direct = 1;
170            default:        /* could consider as an error, but just ignore */
171                ;
172            }
173        mode++;
174    }
175
176    /* must provide an "r", "w", or "a" */
177    if (state->mode == GZ_NONE) {
178        free(state);
179        return NULL;
180    }
181
182    /* can't force transparent read */
183    if (state->mode == GZ_READ) {
184        if (state->direct) {
185            free(state);
186            return NULL;
187        }
188        state->direct = 1;      /* for empty file */
189    }
190
191    /* save the path name for error messages */
192#ifdef _WIN32
193    if (fd == -2) {
194        len = wcstombs(NULL, path, 0);
195        if (len == (size_t)-1)
196            len = 0;
197    }
198    else
199#endif
200        len = strlen(path);
201    state->path = malloc(len + 1);
202    if (state->path == NULL) {
203        free(state);
204        return NULL;
205    }
206#ifdef _WIN32
207    if (fd == -2)
208        if (len)
209            wcstombs(state->path, path, len + 1);
210        else
211            *(state->path) = 0;
212    else
213#endif
214        strcpy(state->path, path);
215
216    /* compute the flags for open() */
217    oflag =
218#ifdef O_LARGEFILE
219        O_LARGEFILE |
220#endif
221#ifdef O_BINARY
222        O_BINARY |
223#endif
224#ifdef O_CLOEXEC
225        (cloexec ? O_CLOEXEC : 0) |
226#endif
227        (state->mode == GZ_READ ?
228         O_RDONLY :
229         (O_WRONLY | O_CREAT |
230#ifdef O_EXCL
231          (exclusive ? O_EXCL : 0) |
232#endif
233          (state->mode == GZ_WRITE ?
234           O_TRUNC :
235           O_APPEND)));
236
237    /* open the file with the appropriate flags (or just use fd) */
238    state->fd = fd > -1 ? fd : (
239#ifdef _WIN32
240        fd == -2 ? _wopen(path, oflag, 0666) :
241#endif
242        open(path, oflag, 0666));
243    if (state->fd == -1) {
244        free(state->path);
245        free(state);
246        return NULL;
247    }
248    if (state->mode == GZ_APPEND)
249        state->mode = GZ_WRITE;         /* simplify later checks */
250
251    /* save the current position for rewinding (only if reading) */
252    if (state->mode == GZ_READ) {
253        state->start = LSEEK(state->fd, 0, SEEK_CUR);
254        if (state->start == -1) state->start = 0;
255    }
256
257    /* initialize stream */
258    gz_reset(state);
259
260    /* return stream */
261    return (gzFile)state;
262}
263
264/* -- see zlib.h -- */
265gzFile ZEXPORT gzopen(path, mode)
266    const char *path;
267    const char *mode;
268{
269    return gz_open(path, -1, mode);
270}
271
272/* -- see zlib.h -- */
273gzFile ZEXPORT gzopen64(path, mode)
274    const char *path;
275    const char *mode;
276{
277    return gz_open(path, -1, mode);
278}
279
280/* -- see zlib.h -- */
281gzFile ZEXPORT gzdopen(fd, mode)
282    int fd;
283    const char *mode;
284{
285    char *path;         /* identifier for error messages */
286    gzFile gz;
287
288    if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL)
289        return NULL;
290    sprintf(path, "<fd:%d>", fd);   /* for debugging */
291    gz = gz_open(path, fd, mode);
292    free(path);
293    return gz;
294}
295
296/* -- see zlib.h -- */
297#ifdef _WIN32
298gzFile ZEXPORT gzopen_w(path, mode)
299    const wchar_t *path;
300    const char *mode;
301{
302    return gz_open(path, -2, mode);
303}
304#endif
305
306/* -- see zlib.h -- */
307int ZEXPORT gzbuffer(file, size)
308    gzFile file;
309    unsigned size;
310{
311    gz_statep state;
312
313    /* get internal structure and check integrity */
314    if (file == NULL)
315        return -1;
316    state = (gz_statep)file;
317    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
318        return -1;
319
320    /* make sure we haven't already allocated memory */
321    if (state->size != 0)
322        return -1;
323
324    /* check and set requested size */
325    if (size < 2)
326        size = 2;               /* need two bytes to check magic header */
327    state->want = size;
328    return 0;
329}
330
331/* -- see zlib.h -- */
332int ZEXPORT gzrewind(file)
333    gzFile file;
334{
335    gz_statep state;
336
337    /* get internal structure */
338    if (file == NULL)
339        return -1;
340    state = (gz_statep)file;
341
342    /* check that we're reading and that there's no error */
343    if (state->mode != GZ_READ ||
344            (state->err != Z_OK && state->err != Z_BUF_ERROR))
345        return -1;
346
347    /* back up and start over */
348    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
349        return -1;
350    gz_reset(state);
351    return 0;
352}
353
354/* -- see zlib.h -- */
355z_off64_t ZEXPORT gzseek64(file, offset, whence)
356    gzFile file;
357    z_off64_t offset;
358    int whence;
359{
360    unsigned n;
361    z_off64_t ret;
362    gz_statep state;
363
364    /* get internal structure and check integrity */
365    if (file == NULL)
366        return -1;
367    state = (gz_statep)file;
368    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
369        return -1;
370
371    /* check that there's no error */
372    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
373        return -1;
374
375    /* can only seek from start or relative to current position */
376    if (whence != SEEK_SET && whence != SEEK_CUR)
377        return -1;
378
379    /* normalize offset to a SEEK_CUR specification */
380    if (whence == SEEK_SET)
381        offset -= state->x.pos;
382    else if (state->seek)
383        offset += state->skip;
384    state->seek = 0;
385
386    /* if within raw area while reading, just go there */
387    if (state->mode == GZ_READ && state->how == COPY &&
388            state->x.pos + offset >= 0) {
389        ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);
390        if (ret == -1)
391            return -1;
392        state->x.have = 0;
393        state->eof = 0;
394        state->past = 0;
395        state->seek = 0;
396        gz_error(state, Z_OK, NULL);
397        state->strm.avail_in = 0;
398        state->x.pos += offset;
399        return state->x.pos;
400    }
401
402    /* calculate skip amount, rewinding if needed for back seek when reading */
403    if (offset < 0) {
404        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
405            return -1;
406        offset += state->x.pos;
407        if (offset < 0)                     /* before start of file! */
408            return -1;
409        if (gzrewind(file) == -1)           /* rewind, then skip to offset */
410            return -1;
411    }
412
413    /* if reading, skip what's in output buffer (one less gzgetc() check) */
414    if (state->mode == GZ_READ) {
415        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
416            (unsigned)offset : state->x.have;
417        state->x.have -= n;
418        state->x.next += n;
419        state->x.pos += n;
420        offset -= n;
421    }
422
423    /* request skip (if not zero) */
424    if (offset) {
425        state->seek = 1;
426        state->skip = offset;
427    }
428    return state->x.pos + offset;
429}
430
431/* -- see zlib.h -- */
432z_off_t ZEXPORT gzseek(file, offset, whence)
433    gzFile file;
434    z_off_t offset;
435    int whence;
436{
437    z_off64_t ret;
438
439    ret = gzseek64(file, (z_off64_t)offset, whence);
440    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
441}
442
443/* -- see zlib.h -- */
444z_off64_t ZEXPORT gztell64(file)
445    gzFile file;
446{
447    gz_statep state;
448
449    /* get internal structure and check integrity */
450    if (file == NULL)
451        return -1;
452    state = (gz_statep)file;
453    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
454        return -1;
455
456    /* return position */
457    return state->x.pos + (state->seek ? state->skip : 0);
458}
459
460/* -- see zlib.h -- */
461z_off_t ZEXPORT gztell(file)
462    gzFile file;
463{
464    z_off64_t ret;
465
466    ret = gztell64(file);
467    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
468}
469
470/* -- see zlib.h -- */
471z_off64_t ZEXPORT gzoffset64(file)
472    gzFile file;
473{
474    z_off64_t offset;
475    gz_statep state;
476
477    /* get internal structure and check integrity */
478    if (file == NULL)
479        return -1;
480    state = (gz_statep)file;
481    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
482        return -1;
483
484    /* compute and return effective offset in file */
485    offset = LSEEK(state->fd, 0, SEEK_CUR);
486    if (offset == -1)
487        return -1;
488    if (state->mode == GZ_READ)             /* reading */
489        offset -= state->strm.avail_in;     /* don't count buffered input */
490    return offset;
491}
492
493/* -- see zlib.h -- */
494z_off_t ZEXPORT gzoffset(file)
495    gzFile file;
496{
497    z_off64_t ret;
498
499    ret = gzoffset64(file);
500    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
501}
502
503/* -- see zlib.h -- */
504int ZEXPORT gzeof(file)
505    gzFile file;
506{
507    gz_statep state;
508
509    /* get internal structure and check integrity */
510    if (file == NULL)
511        return 0;
512    state = (gz_statep)file;
513    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
514        return 0;
515
516    /* return end-of-file state */
517    return state->mode == GZ_READ ? state->past : 0;
518}
519
520/* -- see zlib.h -- */
521const char * ZEXPORT gzerror(file, errnum)
522    gzFile file;
523    int *errnum;
524{
525    gz_statep state;
526
527    /* get internal structure and check integrity */
528    if (file == NULL)
529        return NULL;
530    state = (gz_statep)file;
531    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
532        return NULL;
533
534    /* return error information */
535    if (errnum != NULL)
536        *errnum = state->err;
537    return state->msg == NULL ? "" : state->msg;
538}
539
540/* -- see zlib.h -- */
541void ZEXPORT gzclearerr(file)
542    gzFile file;
543{
544    gz_statep state;
545
546    /* get internal structure and check integrity */
547    if (file == NULL)
548        return;
549    state = (gz_statep)file;
550    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
551        return;
552
553    /* clear error and end-of-file */
554    if (state->mode == GZ_READ) {
555        state->eof = 0;
556        state->past = 0;
557    }
558    gz_error(state, Z_OK, NULL);
559}
560
561/* Create an error message in allocated memory and set state->err and
562   state->msg accordingly.  Free any previous error message already there.  Do
563   not try to free or allocate space if the error is Z_MEM_ERROR (out of
564   memory).  Simply save the error message as a static string.  If there is an
565   allocation failure constructing the error message, then convert the error to
566   out of memory. */
567void ZLIB_INTERNAL gz_error(state, err, msg)
568    gz_statep state;
569    int err;
570    const char *msg;
571{
572    /* free previously allocated message and clear */
573    if (state->msg != NULL) {
574        if (state->err != Z_MEM_ERROR)
575            free(state->msg);
576        state->msg = NULL;
577    }
578
579    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
580    if (err != Z_OK && err != Z_BUF_ERROR)
581        state->x.have = 0;
582
583    /* set error code, and if no message, then done */
584    state->err = err;
585    if (msg == NULL)
586        return;
587
588    /* for an out of memory error, save as static string */
589    if (err == Z_MEM_ERROR) {
590        state->msg = (char *)msg;
591        return;
592    }
593
594    /* construct error message with path */
595    if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
596        state->err = Z_MEM_ERROR;
597        state->msg = (char *)"out of memory";
598        return;
599    }
600    strcpy(state->msg, state->path);
601    strcat(state->msg, ": ");
602    strcat(state->msg, msg);
603    return;
604}
605
606#ifndef INT_MAX
607/* portably return maximum value for an int (when limits.h presumed not
608   available) -- we need to do this to cover cases where 2's complement not
609   used, since C standard permits 1's complement and sign-bit representations,
610   otherwise we could just use ((unsigned)-1) >> 1 */
611unsigned ZLIB_INTERNAL gz_intmax()
612{
613    unsigned p, q;
614
615    p = 1;
616    do {
617        q = p;
618        p <<= 1;
619        p++;
620    } while (p > q);
621    return q >> 1;
622}
623#endif
624