Coverage Report

Created: 2026-10-28 07:26

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