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