Coverage Report

Created: 2025-08-28 06:40

/src/zlib-ng/build/gzread.c
Line
Count
Source (jump to first uncovered line)
1
/* gzread.c -- zlib functions for reading gzip files
2
 * Copyright (C) 2004-2017 Mark Adler
3
 * For conditions of distribution and use, see copyright notice in zlib.h
4
 */
5
6
#include "zbuild.h"
7
#include "zutil_p.h"
8
#include "gzguts.h"
9
10
/* Local functions */
11
static int gz_read_init(gz_state *state);
12
static int gz_load(gz_state *, unsigned char *, unsigned, unsigned *);
13
static int gz_avail(gz_state *);
14
static int gz_look(gz_state *);
15
static int gz_decomp(gz_state *);
16
static int gz_fetch(gz_state *);
17
static int gz_skip(gz_state *, z_off64_t);
18
static size_t gz_read(gz_state *, void *, size_t);
19
20
5.48k
static int gz_read_init(gz_state *state) {
21
    /* Allocate gz buffers */
22
5.48k
    if (gz_buffer_alloc(state) != 0) {
23
0
        gz_error(state, Z_MEM_ERROR, "out of memory");
24
0
        return -1;
25
0
    }
26
27
    /* Initialize inflate state */
28
5.48k
    int ret = PREFIX(inflateInit2)(&(state->strm), MAX_WBITS + 16);
29
5.48k
    if (ret != Z_OK) {
30
0
        gz_buffer_free(state);
31
0
        if (ret == Z_MEM_ERROR) {
32
0
            gz_error(state, Z_MEM_ERROR, "out of memory");
33
0
        } else {
34
0
            gz_error(state, Z_STREAM_ERROR, "invalid compression parameters");
35
0
        }
36
0
        return -1;
37
0
    }
38
5.48k
    return 0;
39
5.48k
}
40
41
/* Use read() to load a buffer -- return -1 on error, otherwise 0.  Read from
42
   state->fd, and update state->eof, state->err, and state->msg as appropriate.
43
   This function needs to loop on read(), since read() is not guaranteed to
44
   read the number of bytes requested, depending on the type of descriptor. */
45
6.85k
static int gz_load(gz_state *state, unsigned char *buf, unsigned len, unsigned *have) {
46
6.85k
    ssize_t ret;
47
48
6.85k
    *have = 0;
49
12.3k
    do {
50
12.3k
        ret = read(state->fd, buf + *have, len - *have);
51
12.3k
        if (ret <= 0)
52
5.48k
            break;
53
6.85k
        *have += (unsigned)ret;
54
6.85k
    } while (*have < len);
55
6.85k
    if (ret < 0) {
56
0
        gz_error(state, Z_ERRNO, zstrerror());
57
0
        return -1;
58
0
    }
59
6.85k
    if (ret == 0)
60
5.48k
        state->eof = 1;
61
6.85k
    return 0;
62
6.85k
}
63
64
/* Load up input buffer and set eof flag if last data loaded -- return -1 on
65
   error, 0 otherwise.  Note that the eof flag is set when the end of the input
66
   file is reached, even though there may be unused data in the buffer.  Once
67
   that data has been used, no more attempts will be made to read the file.
68
   If strm->avail_in != 0, then the current data is moved to the beginning of
69
   the input buffer, and then the remainder of the buffer is loaded with the
70
   available data from the input file. */
71
6.85k
static int gz_avail(gz_state *state) {
72
6.85k
    unsigned got;
73
6.85k
    PREFIX3(stream) *strm = &(state->strm);
74
75
6.85k
    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
76
0
        return -1;
77
6.85k
    if (state->eof == 0) {
78
6.85k
        if (strm->avail_in) {       /* copy what's there to the start */
79
0
            unsigned char *p = state->in;
80
0
            unsigned const char *q = strm->next_in;
81
0
            unsigned n = strm->avail_in;
82
0
            do {
83
0
                *p++ = *q++;
84
0
            } while (--n);
85
0
        }
86
6.85k
        if (gz_load(state, state->in + strm->avail_in, state->size - strm->avail_in, &got) == -1)
87
0
            return -1;
88
6.85k
        strm->avail_in += got;
89
6.85k
        strm->next_in = state->in;
90
6.85k
    }
91
6.85k
    return 0;
92
6.85k
}
93
94
/* Look for gzip header, set up for inflate or copy.  state->x.have must be 0.
95
   If this is the first time in, allocate required memory.  state->how will be
96
   left unchanged if there is no more input data available, will be set to COPY
97
   if there is no gzip header and direct copying will be performed, or it will
98
   be set to GZIP for decompression.  If direct copying, then leftover input
99
   data from the input buffer will be copied to the output buffer.  In that
100
   case, all further file reads will be directly to either the output buffer or
101
   a user buffer.  If decompressing, the inflate state will be initialized.
102
   gz_look() will return 0 on success or -1 on failure. */
103
5.48k
static int gz_look(gz_state *state) {
104
5.48k
    PREFIX3(stream) *strm = &(state->strm);
105
106
    /* allocate memory if this is the first time through */
107
5.48k
    if (state->size == 0 && gz_read_init(state) == -1)
108
0
        return -1;
109
110
    /* get at least the magic bytes in the input buffer */
111
5.48k
    if (strm->avail_in < 2) {
112
5.48k
        if (gz_avail(state) == -1)
113
0
            return -1;
114
5.48k
        if (strm->avail_in == 0)
115
4
            return 0;
116
5.48k
    }
117
118
    /* look for gzip magic bytes -- if there, do gzip decoding (note: there is
119
       a logical dilemma here when considering the case of a partially written
120
       gzip file, to wit, if a single 31 byte is written, then we cannot tell
121
       whether this is a single-byte file, or just a partially written gzip
122
       file -- for here we assume that if a gzip file is being written, then
123
       the header will be written in a single operation, so that reading a
124
       single byte is sufficient indication that it is not a gzip file) */
125
5.48k
    if (strm->avail_in > 1 &&
126
5.48k
            strm->next_in[0] == 31 && strm->next_in[1] == 139) {
127
5.48k
        PREFIX(inflateReset)(strm);
128
5.48k
        state->how = GZIP;
129
5.48k
        state->direct = 0;
130
5.48k
        return 0;
131
5.48k
    }
132
133
    /* no gzip header -- if we were decoding gzip before, then this is trailing
134
       garbage.  Ignore the trailing garbage and finish. */
135
0
    if (state->direct == 0) {
136
0
        strm->avail_in = 0;
137
0
        state->eof = 1;
138
0
        state->x.have = 0;
139
0
        return 0;
140
0
    }
141
142
    /* doing raw i/o, copy any leftover input to output -- this assumes that
143
       the output buffer is larger than the input buffer, which also assures
144
       space for gzungetc() */
145
0
    state->x.next = state->out;
146
0
    memcpy(state->x.next, strm->next_in, strm->avail_in);
147
0
    state->x.have = strm->avail_in;
148
0
    strm->avail_in = 0;
149
0
    state->how = COPY;
150
0
    state->direct = 1;
151
0
    return 0;
152
0
}
153
154
/* Decompress from input to the provided next_out and avail_out in the state.
155
   On return, state->x.have and state->x.next point to the just decompressed
156
   data.  If the gzip stream completes, state->how is reset to LOOK to look for
157
   the next gzip stream or raw data, once state->x.have is depleted.  Returns 0
158
   on success, -1 on failure. */
159
6.30k
static int gz_decomp(gz_state *state) {
160
6.30k
    int ret = Z_OK;
161
6.30k
    unsigned had;
162
6.30k
    PREFIX3(stream) *strm = &(state->strm);
163
164
    /* fill output buffer up to end of deflate stream */
165
6.30k
    had = strm->avail_out;
166
7.67k
    do {
167
        /* get more input for inflate() */
168
7.67k
        if (strm->avail_in == 0 && gz_avail(state) == -1)
169
0
            return -1;
170
7.67k
        if (strm->avail_in == 0) {
171
0
            gz_error(state, Z_BUF_ERROR, "unexpected end of file");
172
0
            break;
173
0
        }
174
175
        /* decompress and handle errors */
176
7.67k
        ret = PREFIX(inflate)(strm, Z_NO_FLUSH);
177
7.67k
        if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
178
0
            gz_error(state, Z_STREAM_ERROR, "internal error: inflate stream corrupt");
179
0
            return -1;
180
0
        }
181
7.67k
        if (ret == Z_MEM_ERROR) {
182
0
            gz_error(state, Z_MEM_ERROR, "out of memory");
183
0
            return -1;
184
0
        }
185
7.67k
        if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
186
0
            gz_error(state, Z_DATA_ERROR, strm->msg == NULL ? "compressed data error" : strm->msg);
187
0
            return -1;
188
0
        }
189
7.67k
    } while (strm->avail_out && ret != Z_STREAM_END);
190
191
    /* update available output */
192
6.30k
    state->x.have = had - strm->avail_out;
193
6.30k
    state->x.next = strm->next_out - state->x.have;
194
195
    /* if the gzip stream completed successfully, look for another */
196
6.30k
    if (ret == Z_STREAM_END)
197
5.48k
        state->how = LOOK;
198
199
    /* good decompression */
200
6.30k
    return 0;
201
6.30k
}
202
203
/* Fetch data and put it in the output buffer.  Assumes state->x.have is 0.
204
   Data is either copied from the input file or decompressed from the input
205
   file depending on state->how.  If state->how is LOOK, then a gzip header is
206
   looked for to determine whether to copy or decompress.  Returns -1 on error,
207
   otherwise 0.  gz_fetch() will leave state->how as COPY or GZIP unless the
208
   end of the input file has been reached and all data has been processed.  */
209
6.30k
static int gz_fetch(gz_state *state) {
210
6.30k
    PREFIX3(stream) *strm = &(state->strm);
211
212
11.7k
    do {
213
11.7k
        switch (state->how) {
214
5.48k
        case LOOK:      /* -> LOOK, COPY (only if never GZIP), or GZIP */
215
5.48k
            if (gz_look(state) == -1)
216
0
                return -1;
217
5.48k
            if (state->how == LOOK)
218
4
                return 0;
219
5.48k
            break;
220
5.48k
        case COPY:      /* -> COPY */
221
0
            if (gz_load(state, state->out, state->size << 1, &(state->x.have))
222
0
                    == -1)
223
0
                return -1;
224
0
            state->x.next = state->out;
225
0
            return 0;
226
6.30k
        case GZIP:      /* -> GZIP or LOOK (if end of gzip stream) */
227
6.30k
            strm->avail_out = state->size << 1;
228
6.30k
            strm->next_out = state->out;
229
6.30k
            if (gz_decomp(state) == -1)
230
0
                return -1;
231
11.7k
        }
232
11.7k
    } while (state->x.have == 0 && (!state->eof || strm->avail_in));
233
6.30k
    return 0;
234
6.30k
}
235
236
/* Skip len uncompressed bytes of output.  Return -1 on error, 0 on success. */
237
0
static int gz_skip(gz_state *state, z_off64_t len) {
238
0
    unsigned n;
239
240
    /* skip over len bytes or reach end-of-file, whichever comes first */
241
0
    while (len)
242
        /* skip over whatever is in output buffer */
243
0
        if (state->x.have) {
244
0
            n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ?
245
0
                (unsigned)len : state->x.have;
246
0
            state->x.have -= n;
247
0
            state->x.next += n;
248
0
            state->x.pos += n;
249
0
            len -= n;
250
0
        } else if (state->eof && state->strm.avail_in == 0) {
251
            /* output buffer empty -- return if we're at the end of the input */
252
0
            break;
253
0
        } else {
254
            /* need more data to skip -- load up output buffer */
255
            /* get more output, looking for header if required */
256
0
            if (gz_fetch(state) == -1)
257
0
                return -1;
258
0
        }
259
0
    return 0;
260
0
}
261
262
/* Read len bytes into buf from file, or less than len up to the end of the
263
   input.  Return the number of bytes read.  If zero is returned, either the
264
   end of file was reached, or there was an error.  state->err must be
265
   consulted in that case to determine which. */
266
17.8k
static size_t gz_read(gz_state *state, void *buf, size_t len) {
267
17.8k
    size_t got;
268
17.8k
    unsigned n;
269
270
    /* if len is zero, avoid unnecessary operations */
271
17.8k
    if (len == 0)
272
0
        return 0;
273
274
    /* process a skip request */
275
17.8k
    if (state->seek) {
276
0
        state->seek = 0;
277
0
        if (gz_skip(state, state->skip) == -1)
278
0
            return 0;
279
0
    }
280
281
    /* get len bytes to buf, or less than len if at the end */
282
17.8k
    got = 0;
283
30.3k
    do {
284
        /* set n to the maximum amount of len that fits in an unsigned int */
285
30.3k
        n = (unsigned)-1;
286
30.3k
        if (n > len)
287
30.3k
            n = (unsigned)len;
288
289
        /* first just try copying data from the output buffer */
290
30.3k
        if (state->x.have) {
291
13.1k
            if (state->x.have < n)
292
6.19k
                n = state->x.have;
293
13.1k
            memcpy(buf, state->x.next, n);
294
13.1k
            state->x.next += n;
295
13.1k
            state->x.have -= n;
296
13.1k
        }
297
298
        /* output buffer empty -- return if we're at the end of the input */
299
17.2k
        else if (state->eof && state->strm.avail_in == 0) {
300
10.9k
            state->past = 1;        /* tried to read past end */
301
10.9k
            break;
302
10.9k
        }
303
304
        /* need output data -- for small len or new stream load up our output
305
           buffer */
306
6.30k
        else if (state->how == LOOK || n < (state->size << 1)) {
307
            /* get more output, looking for header if required */
308
6.30k
            if (gz_fetch(state) == -1)
309
0
                return 0;
310
6.30k
            continue;       /* no progress yet -- go back to copy above */
311
            /* the copy above assures that we will leave with space in the
312
               output buffer, allowing at least one gzungetc() to succeed */
313
6.30k
        }
314
315
        /* large len -- read directly into user buffer */
316
0
        else if (state->how == COPY) {      /* read directly */
317
0
            if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
318
0
                return 0;
319
0
        }
320
321
        /* large len -- decompress directly into user buffer */
322
0
        else {  /* state->how == GZIP */
323
0
            state->strm.avail_out = n;
324
0
            state->strm.next_out = (unsigned char *)buf;
325
0
            if (gz_decomp(state) == -1)
326
0
                return 0;
327
0
            n = state->x.have;
328
0
            state->x.have = 0;
329
0
        }
330
331
        /* update progress */
332
13.1k
        len -= n;
333
13.1k
        buf = (char *)buf + n;
334
13.1k
        got += n;
335
13.1k
        state->x.pos += n;
336
19.4k
    } while (len);
337
338
    /* return number of bytes read into user buffer */
339
17.8k
    return got;
340
17.8k
}
341
342
/* -- see zlib.h -- */
343
17.8k
int Z_EXPORT PREFIX(gzread)(gzFile file, void *buf, unsigned len) {
344
17.8k
    gz_state *state;
345
346
    /* get internal structure */
347
17.8k
    if (file == NULL)
348
0
        return -1;
349
17.8k
    state = (gz_state *)file;
350
351
    /* check that we're reading and that there's no (serious) error */
352
17.8k
    if (state->mode != GZ_READ ||
353
17.8k
            (state->err != Z_OK && state->err != Z_BUF_ERROR))
354
0
        return -1;
355
356
    /* since an int is returned, make sure len fits in one, otherwise return
357
       with an error (this avoids a flaw in the interface) */
358
17.8k
    if ((int)len < 0) {
359
0
        gz_error(state, Z_STREAM_ERROR, "request does not fit in an int");
360
0
        return -1;
361
0
    }
362
363
    /* read len or fewer bytes to buf */
364
17.8k
    len = (unsigned)gz_read(state, buf, len);
365
366
    /* check for an error */
367
17.8k
    if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
368
0
        return -1;
369
370
    /* return the number of bytes read (this is assured to fit in an int) */
371
17.8k
    return (int)len;
372
17.8k
}
373
374
/* -- see zlib.h -- */
375
0
size_t Z_EXPORT PREFIX(gzfread)(void *buf, size_t size, size_t nitems, gzFile file) {
376
0
    size_t len;
377
0
    gz_state *state;
378
379
    /* Exit early if size is zero, also prevents potential division by zero */
380
0
    if (size == 0)
381
0
        return 0;
382
383
    /* get internal structure */
384
0
    if (file == NULL)
385
0
        return 0;
386
0
    state = (gz_state *)file;
387
388
    /* check that we're reading and that there's no (serious) error */
389
0
    if (state->mode != GZ_READ ||
390
0
            (state->err != Z_OK && state->err != Z_BUF_ERROR))
391
0
        return 0;
392
393
    /* compute bytes to read -- error on overflow */
394
0
    if (size && SIZE_MAX / size < nitems) {
395
0
        gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
396
0
        return 0;
397
0
    }
398
0
    len = nitems * size;
399
400
    /* read len or fewer bytes to buf, return the number of full items read */
401
0
    return len ? gz_read(state, buf, len) / size : 0;
402
0
}
403
404
/* -- see zlib.h -- */
405
#undef gzgetc
406
#undef zng_gzgetc
407
0
int Z_EXPORT PREFIX(gzgetc)(gzFile file) {
408
0
    unsigned char buf[1];
409
0
    gz_state *state;
410
411
    /* get internal structure */
412
0
    if (file == NULL)
413
0
        return -1;
414
0
    state = (gz_state *)file;
415
416
    /* check that we're reading and that there's no (serious) error */
417
0
    if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR))
418
0
        return -1;
419
420
    /* try output buffer (no need to check for skip request) */
421
0
    if (state->x.have) {
422
0
        state->x.have--;
423
0
        state->x.pos++;
424
0
        return *(state->x.next)++;
425
0
    }
426
427
    /* nothing there -- try gz_read() */
428
0
    return gz_read(state, buf, 1) < 1 ? -1 : buf[0];
429
0
}
430
431
#ifdef ZLIB_COMPAT
432
int Z_EXPORT PREFIX(gzgetc_)(gzFile file) {
433
    return PREFIX(gzgetc)(file);
434
}
435
#endif
436
437
/* -- see zlib.h -- */
438
0
int Z_EXPORT PREFIX(gzungetc)(int c, gzFile file) {
439
0
    gz_state *state;
440
441
    /* get internal structure */
442
0
    if (file == NULL)
443
0
        return -1;
444
0
    state = (gz_state *)file;
445
446
    /* in case this was just opened, set up the input buffer */
447
0
    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
448
0
        (void)gz_look(state);
449
450
    /* check that we're reading and that there's no (serious) error */
451
0
    if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR))
452
0
        return -1;
453
454
    /* process a skip request */
455
0
    if (state->seek) {
456
0
        state->seek = 0;
457
0
        if (gz_skip(state, state->skip) == -1)
458
0
            return -1;
459
0
    }
460
461
    /* can't push EOF */
462
0
    if (c < 0)
463
0
        return -1;
464
465
    /* if output buffer empty, put byte at end (allows more pushing) */
466
0
    if (state->x.have == 0) {
467
0
        state->x.have = 1;
468
0
        state->x.next = state->out + (state->size << 1) - 1;
469
0
        state->x.next[0] = (unsigned char)c;
470
0
        state->x.pos--;
471
0
        state->past = 0;
472
0
        return c;
473
0
    }
474
475
    /* if no room, give up (must have already done a gzungetc()) */
476
0
    if (state->x.have == (state->size << 1)) {
477
0
        gz_error(state, Z_DATA_ERROR, "out of room to push characters");
478
0
        return -1;
479
0
    }
480
481
    /* slide output data if needed and insert byte before existing data */
482
0
    if (state->x.next == state->out) {
483
0
        unsigned char *src = state->out + state->x.have;
484
0
        unsigned char *dest = state->out + (state->size << 1);
485
0
        while (src > state->out)
486
0
            *--dest = *--src;
487
0
        state->x.next = dest;
488
0
    }
489
0
    state->x.have++;
490
0
    state->x.next--;
491
0
    state->x.next[0] = (unsigned char)c;
492
0
    state->x.pos--;
493
0
    state->past = 0;
494
0
    return c;
495
0
}
496
497
/* -- see zlib.h -- */
498
0
char * Z_EXPORT PREFIX(gzgets)(gzFile file, char *buf, int len) {
499
0
    unsigned left, n;
500
0
    char *str;
501
0
    unsigned char *eol;
502
0
    gz_state *state;
503
504
    /* check parameters and get internal structure */
505
0
    if (file == NULL || buf == NULL || len < 1)
506
0
        return NULL;
507
0
    state = (gz_state *)file;
508
509
    /* check that we're reading and that there's no (serious) error */
510
0
    if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR))
511
0
        return NULL;
512
513
    /* process a skip request */
514
0
    if (state->seek) {
515
0
        state->seek = 0;
516
0
        if (gz_skip(state, state->skip) == -1)
517
0
            return NULL;
518
0
    }
519
520
    /* copy output bytes up to new line or len - 1, whichever comes first --
521
       append a terminating zero to the string (we don't check for a zero in
522
       the contents, let the user worry about that) */
523
0
    str = buf;
524
0
    left = (unsigned)len - 1;
525
0
    if (left) {
526
0
        do {
527
            /* assure that something is in the output buffer */
528
0
            if (state->x.have == 0 && gz_fetch(state) == -1)
529
0
                return NULL;                /* error */
530
0
            if (state->x.have == 0) {       /* end of file */
531
0
                state->past = 1;            /* read past end */
532
0
                break;                      /* return what we have */
533
0
            }
534
535
            /* look for end-of-line in current output buffer */
536
0
            n = state->x.have > left ? left : state->x.have;
537
0
            eol = (unsigned char *)memchr(state->x.next, '\n', n);
538
0
            if (eol != NULL)
539
0
                n = (unsigned)(eol - state->x.next) + 1;
540
541
            /* copy through end-of-line, or remainder if not found */
542
0
            memcpy(buf, state->x.next, n);
543
0
            state->x.have -= n;
544
0
            state->x.next += n;
545
0
            state->x.pos += n;
546
0
            left -= n;
547
0
            buf += n;
548
0
        } while (left && eol == NULL);
549
0
    }
550
551
    /* return terminated string, or if nothing, end of file */
552
0
    if (buf == str)
553
0
        return NULL;
554
0
    buf[0] = 0;
555
0
    return str;
556
0
}
557
558
/* -- see zlib.h -- */
559
0
int Z_EXPORT PREFIX(gzdirect)(gzFile file) {
560
0
    gz_state *state;
561
562
    /* get internal structure */
563
0
    if (file == NULL)
564
0
        return 0;
565
566
0
    state = (gz_state *)file;
567
568
    /* if the state is not known, but we can find out, then do so (this is
569
       mainly for right after a gzopen() or gzdopen()) */
570
0
    if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
571
0
        (void)gz_look(state);
572
573
    /* return 1 if transparent, 0 if processing a gzip stream */
574
0
    return state->direct;
575
0
}
576
577
/* -- see zlib.h -- */
578
5.48k
int Z_EXPORT PREFIX(gzclose_r)(gzFile file) {
579
5.48k
    int ret, err;
580
5.48k
    gz_state *state;
581
582
    /* get internal structure */
583
5.48k
    if (file == NULL)
584
0
        return Z_STREAM_ERROR;
585
586
5.48k
    state = (gz_state *)file;
587
588
    /* check that we're reading */
589
5.48k
    if (state->mode != GZ_READ)
590
0
        return Z_STREAM_ERROR;
591
592
    /* free memory and close file */
593
5.48k
    if (state->size) {
594
5.48k
        PREFIX(inflateEnd)(&(state->strm));
595
5.48k
        gz_buffer_free(state);
596
5.48k
    }
597
5.48k
    err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;
598
5.48k
    gz_error(state, Z_OK, NULL);
599
5.48k
    free(state->path);
600
5.48k
    ret = close(state->fd);
601
5.48k
    zng_free(state);
602
5.48k
    return ret ? Z_ERRNO : err;
603
5.48k
}