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