Add zlib gzip functions

These will now be included in the cosmopolitan.a releases. It took a bit
of time because, these functions depend on heavyweight parts of the libc
that wouldn't be appropriate for the core zlib library to depend upon.

Fixes #345
This commit is contained in:
Justine Tunney 2022-05-12 12:16:25 -07:00
parent 4499f98e76
commit 4e62cefa6e
11 changed files with 1991 additions and 8 deletions

View file

@ -127,6 +127,7 @@ include third_party/stb/stb.mk # │
include dsp/scale/scale.mk # │
include dsp/mpeg/mpeg.mk # │
include dsp/dsp.mk # │
include third_party/zlib/gz/gz.mk # │
include third_party/musl/musl.mk # │
include third_party/getopt/getopt.mk # │
include libc/libc.mk #─┘
@ -335,6 +336,7 @@ COSMOPOLITAN_HEADERS = \
THIRD_PARTY_GETOPT \
THIRD_PARTY_MUSL \
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ \
THIRD_PARTY_REGEX
o/$(MODE)/cosmopolitan.a: \

45
test/libc/stdio/gz_test.c Normal file
View file

@ -0,0 +1,45 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "third_party/zlib/zlib.h"
char testlib_enable_tmp_setup_teardown;
TEST(gz, test) {
int fd;
gzFile f;
char buf[64] = {0};
ASSERT_NE(NULL, (f = gzopen("hello.txt.gz", "wb")));
ASSERT_EQ(5, gzwrite(f, "hello", 5));
ASSERT_EQ(Z_OK, gzclose(f));
ASSERT_NE(NULL, (f = gzopen("hello.txt.gz", "rb")));
ASSERT_EQ(5, gzread(f, buf, sizeof(buf)));
ASSERT_STREQ("hello", buf);
ASSERT_EQ(Z_OK, gzclose(f));
ASSERT_NE(NULL, (fd = open("hello.txt.gz", O_RDONLY)));
ASSERT_EQ(25, read(fd, buf, sizeof(buf)));
EXPECT_BINEQ(u"▼ï◘      ♥╦H═╔╔• åª►6♣   ", buf);
ASSERT_SYS(0, 0, close(fd));
}

View file

@ -41,7 +41,8 @@ TEST_LIBC_STDIO_DIRECTDEPS = \
LIBC_UNICODE \
LIBC_X \
LIBC_ZIPOS \
THIRD_PARTY_ZLIB
THIRD_PARTY_ZLIB \
THIRD_PARTY_ZLIB_GZ
TEST_LIBC_STDIO_DEPS := \
$(call uniq,$(foreach x,$(TEST_LIBC_STDIO_DIRECTDEPS),$($(x))))

58
third_party/zlib/gz/gz.mk vendored Normal file
View file

@ -0,0 +1,58 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += THIRD_PARTY_ZLIB_GZ
THIRD_PARTY_ZLIB_GZ_ARTIFACTS += THIRD_PARTY_ZLIB_GZ_A
THIRD_PARTY_ZLIB_GZ = $(THIRD_PARTY_ZLIB_GZ_A_DEPS) $(THIRD_PARTY_ZLIB_GZ_A)
THIRD_PARTY_ZLIB_GZ_A = o/$(MODE)/third_party/zlib/gz/gz.a
THIRD_PARTY_ZLIB_GZ_A_FILES := $(wildcard third_party/zlib/gz/*)
THIRD_PARTY_ZLIB_GZ_A_HDRS = $(filter %.h,$(THIRD_PARTY_ZLIB_GZ_A_FILES))
THIRD_PARTY_ZLIB_GZ_A_SRCS = $(filter %.c,$(THIRD_PARTY_ZLIB_GZ_A_FILES))
THIRD_PARTY_ZLIB_GZ_A_OBJS = $(THIRD_PARTY_ZLIB_GZ_A_SRCS:%.c=o/$(MODE)/%.o)
THIRD_PARTY_ZLIB_GZ_A_CHECKS = \
$(THIRD_PARTY_ZLIB_GZ_A).pkg \
$(THIRD_PARTY_ZLIB_GZ_A_HDRS:%=o/$(MODE)/%.ok)
THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
THIRD_PARTY_ZLIB
THIRD_PARTY_ZLIB_GZ_A_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS),$($(x))))
$(THIRD_PARTY_ZLIB_GZ_A): \
third_party/zlib/gz/ \
$(THIRD_PARTY_ZLIB_GZ_A).pkg \
$(THIRD_PARTY_ZLIB_GZ_A_OBJS)
$(THIRD_PARTY_ZLIB_GZ_A).pkg: \
$(THIRD_PARTY_ZLIB_GZ_A_OBJS) \
$(foreach x,$(THIRD_PARTY_ZLIB_GZ_A_DIRECTDEPS),$($(x)_A).pkg)
$(THIRD_PARTY_ZLIB_GZ_OBJS): \
OVERRIDE_CFLAGS += \
-ffunction-sections \
-fdata-sections
THIRD_PARTY_ZLIB_GZ_LIBS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)))
THIRD_PARTY_ZLIB_GZ_SRCS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_ZLIB_GZ_HDRS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_HDRS))
THIRD_PARTY_ZLIB_GZ_BINS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_BINS))
THIRD_PARTY_ZLIB_GZ_CHECKS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_CHECKS))
THIRD_PARTY_ZLIB_GZ_OBJS = $(foreach x,$(THIRD_PARTY_ZLIB_GZ_ARTIFACTS),$($(x)_OBJS))
$(THIRD_PARTY_ZLIB_GZ_OBJS): $(BUILD_FILES) third_party/zlib/gz/gz.mk
.PHONY: o/$(MODE)/third_party/zlib/gz
o/$(MODE)/third_party/zlib/gz: \
$(THIRD_PARTY_ZLIB_GZ_A) \
$(THIRD_PARTY_ZLIB_GZ_CHECKS)

27
third_party/zlib/gz/gzclose.c vendored Normal file
View file

@ -0,0 +1,27 @@
#include "third_party/zlib/gz/gzguts.inc"
// clang-format off
/* gzclose.c -- zlib gzclose() function
* Copyright (C) 2004, 2010 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
asm(".ident\t\"\\n\\n\
zlib (zlib License)\\n\
Copyright 1995-2017 Jean-loup Gailly and Mark Adler\"");
asm(".include \"libc/disclaimer.inc\"");
/* gzclose() is in a separate file so that it is linked in only if it is used.
That way the other gzclose functions can be used instead to avoid linking in
unneeded compression or decompression routines. */
int gzclose(file)
gzFile file;
{
gz_statep state;
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file);
}

115
third_party/zlib/gz/gzguts.inc vendored Normal file
View file

@ -0,0 +1,115 @@
#include "libc/errno.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#include "third_party/zlib/zlib.h"
// clang-format off
/* gzguts.h -- zlib internal header definitions for gz* operations
* Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#ifdef _LARGEFILE64_SOURCE
# ifndef _LARGEFILE_SOURCE
# define _LARGEFILE_SOURCE 1
# endif
# ifdef _FILE_OFFSET_BITS
# undef _FILE_OFFSET_BITS
# endif
#endif
#define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
#define _POSIX_SOURCE
#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
# ifndef HAVE_VSNPRINTF
# define HAVE_VSNPRINTF
# endif
#endif
/* since "static" is used to mean two completely different things in C, we
define "local" for the non-static meaning of "static", for readability
(compile with -Dlocal if your debugger can't find static symbols) */
#define zstrerror() strerror(errno)
/* provide prototypes for these when building zlib without LFS */
#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
gzFile gzopen64(const char *, const char *);
int64_t gzseek64(gzFile, int64_t, int);
int64_t gztell64(gzFile);
int64_t gzoffset64(gzFile);
#endif
/* default memLevel */
#if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
#else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
/* default i/o buffer size -- double this for output when reading (this and
twice this must be able to fit in an unsigned type) */
#define GZBUFSIZE 8192
/* gzip modes, also provide a little integrity check on the passed structure */
#define GZ_NONE 0
#define GZ_READ 7247
#define GZ_WRITE 31153
#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */
/* values for gz_state how */
#define LOOK 0 /* look for a gzip header */
#define COPY 1 /* copy input directly */
#define GZIP 2 /* decompress a gzip stream */
/* internal gzip file state data structure */
typedef struct {
/* exposed contents for gzgetc() macro */
struct gzFile_s x; /* "x" for exposed */
/* x.have: number of bytes available at x.next */
/* x.next: next output data to deliver or write */
/* x.pos: current position in uncompressed data */
/* used for both reading and writing */
int mode; /* see gzip modes above */
int fd; /* file descriptor */
char *path; /* path or fd for error messages */
unsigned size; /* buffer size, zero if not allocated yet */
unsigned want; /* requested buffer size, default is GZBUFSIZE */
unsigned char *in; /* input buffer (double-sized when writing) */
unsigned char *out; /* output buffer (double-sized when reading) */
int direct; /* 0 if processing gzip, 1 if transparent */
/* just for reading */
int how; /* 0: get header, 1: copy, 2: decompress */
int64_t start; /* where the gzip data started, for rewinding */
int eof; /* true if end of input file reached */
int past; /* true if read requested past end */
/* just for writing */
int level; /* compression level */
int strategy; /* compression strategy */
/* seek request */
int64_t skip; /* amount to skip (already rewound if backwards) */
int seek; /* true if seek request pending */
/* error information */
int err; /* error code */
char *msg; /* error message */
/* zlib inflate or deflate stream */
z_stream strm; /* stream structure in-place (not a pointer) */
} gz_state;
typedef gz_state *gz_statep;
/* shared functions */
void ZLIB_INTERNAL gz_error (gz_statep, int, const char *);
#if defined UNDER_CE
char ZLIB_INTERNAL *gz_strwinerror (DWORD error);
#endif
/* GT_OFF(x), where x is an unsigned value, is true if x > maximum int64_t
value -- needed when comparing unsigned to int64_t, which is signed
(possible int64_t types off_t, off64_t, and long are all signed) */
#ifdef INT_MAX
# define GT_OFF(x) (sizeof(int) == sizeof(int64_t) && (x) > INT_MAX)
#else
unsigned ZLIB_INTERNAL gz_intmax (void);
# define GT_OFF(x) (sizeof(int) == sizeof(int64_t) && (x) > gz_intmax())
#endif

510
third_party/zlib/gz/gzlib.c vendored Normal file
View file

@ -0,0 +1,510 @@
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "third_party/zlib/gz/gzguts.inc"
#include "third_party/zlib/zlib.h"
// clang-format off
/* gzlib.c -- zlib functions common to reading and writing gzip files
* Copyright (C) 2004-2017 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
asm(".ident\t\"\\n\\n\
zlib (zlib License)\\n\
Copyright 1995-2017 Jean-loup Gailly and Mark Adler\"");
asm(".include \"libc/disclaimer.inc\"");
/* Local functions */
static void gz_reset(gz_statep);
static gzFile gz_open(const void *, int, const char *);
/* Reset gzip file state */
static void gz_reset(state)
gz_statep state;
{
state->x.have = 0; /* no output data available */
if (state->mode == GZ_READ) { /* for reading ... */
state->eof = 0; /* not at end of file */
state->past = 0; /* have not read past end yet */
state->how = LOOK; /* look for gzip header */
}
state->seek = 0; /* no seek request pending */
gz_error(state, Z_OK, NULL); /* clear error */
state->x.pos = 0; /* no uncompressed data yet */
state->strm.avail_in = 0; /* no input data yet */
}
/* Open a gzip file either by name or file descriptor. */
static gzFile gz_open(path, fd, mode)
const void *path;
int fd;
const char *mode;
{
gz_statep state;
size_t len;
int oflag;
int cloexec = 0;
int exclusive = 0;
/* check input */
if (path == NULL)
return NULL;
/* allocate gzFile structure to return */
state = (gz_statep)malloc(sizeof(gz_state));
if (state == NULL)
return NULL;
state->size = 0; /* no buffers allocated yet */
state->want = GZBUFSIZE; /* requested buffer size */
state->msg = NULL; /* no error message yet */
/* interpret mode */
state->mode = GZ_NONE;
state->level = Z_DEFAULT_COMPRESSION;
state->strategy = Z_DEFAULT_STRATEGY;
state->direct = 0;
while (*mode) {
if (*mode >= '0' && *mode <= '9')
state->level = *mode - '0';
else
switch (*mode) {
case 'r':
state->mode = GZ_READ;
break;
case 'w':
state->mode = GZ_WRITE;
break;
case 'a':
state->mode = GZ_APPEND;
break;
case '+': /* can't read and write at the same time */
free(state);
return NULL;
case 'b': /* ignore -- will request binary anyway */
break;
case 'e':
cloexec = 1;
break;
case 'x':
exclusive = 1;
break;
case 'f':
state->strategy = Z_FILTERED;
break;
case 'h':
state->strategy = Z_HUFFMAN_ONLY;
break;
case 'R':
state->strategy = Z_RLE;
break;
case 'F':
state->strategy = Z_FIXED;
break;
case 'T':
state->direct = 1;
break;
default: /* could consider as an error, but just ignore */
;
}
mode++;
}
/* must provide an "r", "w", or "a" */
if (state->mode == GZ_NONE) {
free(state);
return NULL;
}
/* can't force transparent read */
if (state->mode == GZ_READ) {
if (state->direct) {
free(state);
return NULL;
}
state->direct = 1; /* for empty file */
}
/* save the path name for error messages */
len = strlen((const char *)path);
state->path = (char *)malloc(len + 1);
if (state->path == NULL) {
free(state);
return NULL;
}
snprintf(state->path, len + 1, "%s", (const char *)path);
/* compute the flags for open() */
oflag =
(cloexec ? O_CLOEXEC : 0) |
(state->mode == GZ_READ ?
O_RDONLY :
(O_WRONLY | O_CREAT |
(exclusive ? O_EXCL : 0) |
(state->mode == GZ_WRITE ?
O_TRUNC :
O_APPEND)));
/* open the file with the appropriate flags (or just use fd) */
state->fd = fd > -1 ? fd : (
open((const char *)path, oflag, 0666));
if (state->fd == -1) {
free(state->path);
free(state);
return NULL;
}
if (state->mode == GZ_APPEND) {
lseek(state->fd, 0, SEEK_END); /* so gzoffset() is correct */
state->mode = GZ_WRITE; /* simplify later checks */
}
/* save the current position for rewinding (only if reading) */
if (state->mode == GZ_READ) {
state->start = lseek(state->fd, 0, SEEK_CUR);
if (state->start == -1) state->start = 0;
}
/* initialize stream */
gz_reset(state);
/* return stream */
return (gzFile)state;
}
/* -- see zlib.h -- */
gzFile gzopen(path, mode)
const char *path;
const char *mode;
{
return gz_open(path, -1, mode);
}
/* -- see zlib.h -- */
gzFile gzopen64(path, mode)
const char *path;
const char *mode;
{
return gz_open(path, -1, mode);
}
/* -- see zlib.h -- */
gzFile gzdopen(fd, mode)
int fd;
const char *mode;
{
char *path; /* identifier for error messages */
gzFile gz;
if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)
return NULL;
#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
(void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd);
#else
sprintf(path, "<%s:%d>", "fd", fd); /* for debugging */
#endif
gz = gz_open(path, fd, mode);
free(path);
return gz;
}
/* -- see zlib.h -- */
int gzbuffer(file, size)
gzFile file;
unsigned size;
{
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return -1;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return -1;
/* make sure we haven't already allocated memory */
if (state->size != 0)
return -1;
/* check and set requested size */
if ((size << 1) < size)
return -1; /* need to be able to double it */
if (size < 2)
size = 2; /* need two bytes to check magic header */
state->want = size;
return 0;
}
/* -- see zlib.h -- */
int gzrewind(file)
gzFile file;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're reading and that there's no error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* back up and start over */
if (lseek(state->fd, state->start, SEEK_SET) == -1)
return -1;
gz_reset(state);
return 0;
}
/* -- see zlib.h -- */
int64_t gzseek64(file, offset, whence)
gzFile file;
int64_t offset;
int whence;
{
unsigned n;
int64_t ret;
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return -1;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return -1;
/* check that there's no error */
if (state->err != Z_OK && state->err != Z_BUF_ERROR)
return -1;
/* can only seek from start or relative to current position */
if (whence != SEEK_SET && whence != SEEK_CUR)
return -1;
/* normalize offset to a SEEK_CUR specification */
if (whence == SEEK_SET)
offset -= state->x.pos;
else if (state->seek)
offset += state->skip;
state->seek = 0;
/* if within raw area while reading, just go there */
if (state->mode == GZ_READ && state->how == COPY &&
state->x.pos + offset >= 0) {
ret = lseek(state->fd, offset - state->x.have, SEEK_CUR);
if (ret == -1)
return -1;
state->x.have = 0;
state->eof = 0;
state->past = 0;
state->seek = 0;
gz_error(state, Z_OK, NULL);
state->strm.avail_in = 0;
state->x.pos += offset;
return state->x.pos;
}
/* calculate skip amount, rewinding if needed for back seek when reading */
if (offset < 0) {
if (state->mode != GZ_READ) /* writing -- can't go backwards */
return -1;
offset += state->x.pos;
if (offset < 0) /* before start of file! */
return -1;
if (gzrewind(file) == -1) /* rewind, then skip to offset */
return -1;
}
/* if reading, skip what's in output buffer (one less gzgetc() check) */
if (state->mode == GZ_READ) {
n = GT_OFF(state->x.have) || (int64_t)state->x.have > offset ?
(unsigned)offset : state->x.have;
state->x.have -= n;
state->x.next += n;
state->x.pos += n;
offset -= n;
}
/* request skip (if not zero) */
if (offset) {
state->seek = 1;
state->skip = offset;
}
return state->x.pos + offset;
}
/* -- see zlib.h -- */
int64_t gzseek(file, offset, whence)
gzFile file;
int64_t offset;
int whence;
{
int64_t ret;
ret = gzseek64(file, (int64_t)offset, whence);
return ret == (int64_t)ret ? (int64_t)ret : -1;
}
/* -- see zlib.h -- */
int64_t gztell64(file)
gzFile file;
{
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return -1;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return -1;
/* return position */
return state->x.pos + (state->seek ? state->skip : 0);
}
/* -- see zlib.h -- */
int64_t gztell(file)
gzFile file;
{
int64_t ret;
ret = gztell64(file);
return ret == (int64_t)ret ? (int64_t)ret : -1;
}
/* -- see zlib.h -- */
int64_t gzoffset64(file)
gzFile file;
{
int64_t offset;
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return -1;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return -1;
/* compute and return effective offset in file */
offset = lseek(state->fd, 0, SEEK_CUR);
if (offset == -1)
return -1;
if (state->mode == GZ_READ) /* reading */
offset -= state->strm.avail_in; /* don't count buffered input */
return offset;
}
/* -- see zlib.h -- */
int64_t gzoffset(file)
gzFile file;
{
int64_t ret;
ret = gzoffset64(file);
return ret == (int64_t)ret ? (int64_t)ret : -1;
}
/* -- see zlib.h -- */
int gzeof(file)
gzFile file;
{
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return 0;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return 0;
/* return end-of-file state */
return state->mode == GZ_READ ? state->past : 0;
}
/* -- see zlib.h -- */
const char * gzerror(file, errnum)
gzFile file;
int *errnum;
{
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return NULL;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return NULL;
/* return error information */
if (errnum != NULL)
*errnum = state->err;
return state->err == Z_MEM_ERROR ? "out of memory" :
(state->msg == NULL ? "" : state->msg);
}
/* -- see zlib.h -- */
void gzclearerr(file)
gzFile file;
{
gz_statep state;
/* get internal structure and check integrity */
if (file == NULL)
return;
state = (gz_statep)file;
if (state->mode != GZ_READ && state->mode != GZ_WRITE)
return;
/* clear error and end-of-file */
if (state->mode == GZ_READ) {
state->eof = 0;
state->past = 0;
}
gz_error(state, Z_OK, NULL);
}
/* Create an error message in allocated memory and set state->err and
state->msg accordingly. Free any previous error message already there. Do
not try to free or allocate space if the error is Z_MEM_ERROR (out of
memory). Simply save the error message as a static string. If there is an
allocation failure constructing the error message, then convert the error to
out of memory. */
void ZLIB_INTERNAL gz_error(state, err, msg)
gz_statep state;
int err;
const char *msg;
{
/* free previously allocated message and clear */
if (state->msg != NULL) {
if (state->err != Z_MEM_ERROR)
free(state->msg);
state->msg = NULL;
}
/* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
if (err != Z_OK && err != Z_BUF_ERROR)
state->x.have = 0;
/* set error code, and if no message, then done */
state->err = err;
if (msg == NULL)
return;
/* for an out of memory error, return literal string when requested */
if (err == Z_MEM_ERROR)
return;
/* construct error message with path */
if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==
NULL) {
state->err = Z_MEM_ERROR;
return;
}
(void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,
"%s%s%s", state->path, ": ", msg);
}

665
third_party/zlib/gz/gzread.c vendored Normal file
View file

@ -0,0 +1,665 @@
#include "libc/calls/calls.h"
#include "third_party/zlib/gz/gzguts.inc"
// clang-format off
/* gzread.c -- zlib functions for reading gzip files
* Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
asm(".ident\t\"\\n\\n\
zlib (zlib License)\\n\
Copyright 1995-2017 Jean-loup Gailly and Mark Adler\"");
asm(".include \"libc/disclaimer.inc\"");
/* Static functions */
static int gz_load(gz_statep, unsigned char *, unsigned, unsigned *);
static int gz_avail(gz_statep);
static int gz_look(gz_statep);
static int gz_decomp(gz_statep);
static int gz_fetch(gz_statep);
static int gz_skip(gz_statep, int64_t);
static size_t gz_read(gz_statep, voidp, size_t);
/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from
state->fd, and update state->eof, state->err, and state->msg as appropriate.
This function needs to loop on read(), since read() is not guaranteed to
read the number of bytes requested, depending on the type of descriptor. */
static int gz_load(state, buf, len, have)
gz_statep state;
unsigned char *buf;
unsigned len;
unsigned *have;
{
int ret;
unsigned get, max = ((unsigned)-1 >> 2) + 1;
*have = 0;
do {
get = len - *have;
if (get > max)
get = max;
ret = read(state->fd, buf + *have, get);
if (ret <= 0)
break;
*have += (unsigned)ret;
} while (*have < len);
if (ret < 0) {
gz_error(state, Z_ERRNO, zstrerror());
return -1;
}
if (ret == 0)
state->eof = 1;
return 0;
}
/* Load up input buffer and set eof flag if last data loaded -- return -1 on
error, 0 otherwise. Note that the eof flag is set when the end of the input
file is reached, even though there may be unused data in the buffer. Once
that data has been used, no more attempts will be made to read the file.
If strm->avail_in != 0, then the current data is moved to the beginning of
the input buffer, and then the remainder of the buffer is loaded with the
available data from the input file. */
static int gz_avail(state)
gz_statep state;
{
unsigned got;
z_streamp strm = &(state->strm);
if (state->err != Z_OK && state->err != Z_BUF_ERROR)
return -1;
if (state->eof == 0) {
if (strm->avail_in) { /* copy what's there to the start */
unsigned char *p = state->in;
unsigned const char *q = strm->next_in;
unsigned n = strm->avail_in;
do {
*p++ = *q++;
} while (--n);
}
if (gz_load(state, state->in + strm->avail_in,
state->size - strm->avail_in, &got) == -1)
return -1;
strm->avail_in += got;
strm->next_in = state->in;
}
return 0;
}
/* Look for gzip header, set up for inflate or copy. state->x.have must be 0.
If this is the first time in, allocate required memory. state->how will be
left unchanged if there is no more input data available, will be set to COPY
if there is no gzip header and direct copying will be performed, or it will
be set to GZIP for decompression. If direct copying, then leftover input
data from the input buffer will be copied to the output buffer. In that
case, all further file reads will be directly to either the output buffer or
a user buffer. If decompressing, the inflate state will be initialized.
gz_look() will return 0 on success or -1 on failure. */
static int gz_look(state)
gz_statep state;
{
z_streamp strm = &(state->strm);
/* allocate read buffers and inflate memory */
if (state->size == 0) {
/* allocate buffers */
state->in = (unsigned char *)malloc(state->want);
state->out = (unsigned char *)malloc(state->want << 1);
if (state->in == NULL || state->out == NULL) {
free(state->out);
free(state->in);
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
state->size = state->want;
/* allocate inflate memory */
state->strm.zalloc = Z_NULL;
state->strm.zfree = Z_NULL;
state->strm.opaque = Z_NULL;
state->strm.avail_in = 0;
state->strm.next_in = Z_NULL;
if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */
free(state->out);
free(state->in);
state->size = 0;
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
}
/* get at least the magic bytes in the input buffer */
if (strm->avail_in < 2) {
if (gz_avail(state) == -1)
return -1;
if (strm->avail_in == 0)
return 0;
}
/* look for gzip magic bytes -- if there, do gzip decoding (note: there is
a logical dilemma here when considering the case of a partially written
gzip file, to wit, if a single 31 byte is written, then we cannot tell
whether this is a single-byte file, or just a partially written gzip
file -- for here we assume that if a gzip file is being written, then
the header will be written in a single operation, so that reading a
single byte is sufficient indication that it is not a gzip file) */
if (strm->avail_in > 1 &&
strm->next_in[0] == 31 && strm->next_in[1] == 139) {
inflateReset(strm);
state->how = GZIP;
state->direct = 0;
return 0;
}
/* no gzip header -- if we were decoding gzip before, then this is trailing
garbage. Ignore the trailing garbage and finish. */
if (state->direct == 0) {
strm->avail_in = 0;
state->eof = 1;
state->x.have = 0;
return 0;
}
/* doing raw i/o, copy any leftover input to output -- this assumes that
the output buffer is larger than the input buffer, which also assures
space for gzungetc() */
state->x.next = state->out;
if (strm->avail_in) {
memcpy(state->x.next, strm->next_in, strm->avail_in);
state->x.have = strm->avail_in;
strm->avail_in = 0;
}
state->how = COPY;
state->direct = 1;
return 0;
}
/* Decompress from input to the provided next_out and avail_out in the state.
On return, state->x.have and state->x.next point to the just decompressed
data. If the gzip stream completes, state->how is reset to LOOK to look for
the next gzip stream or raw data, once state->x.have is depleted. Returns 0
on success, -1 on failure. */
static int gz_decomp(state)
gz_statep state;
{
int ret = Z_OK;
unsigned had;
z_streamp strm = &(state->strm);
/* fill output buffer up to end of deflate stream */
had = strm->avail_out;
do {
/* get more input for inflate() */
if (strm->avail_in == 0 && gz_avail(state) == -1)
return -1;
if (strm->avail_in == 0) {
gz_error(state, Z_BUF_ERROR, "unexpected end of file");
break;
}
/* decompress and handle errors */
ret = inflate(strm, Z_NO_FLUSH);
if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
gz_error(state, Z_STREAM_ERROR,
"internal error: inflate stream corrupt");
return -1;
}
if (ret == Z_MEM_ERROR) {
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
if (ret == Z_DATA_ERROR) { /* deflate stream invalid */
gz_error(state, Z_DATA_ERROR,
strm->msg == NULL ? "compressed data error" : strm->msg);
return -1;
}
} while (strm->avail_out && ret != Z_STREAM_END);
/* update available output */
state->x.have = had - strm->avail_out;
state->x.next = strm->next_out - state->x.have;
/* if the gzip stream completed successfully, look for another */
if (ret == Z_STREAM_END)
state->how = LOOK;
/* good decompression */
return 0;
}
/* Fetch data and put it in the output buffer. Assumes state->x.have is 0.
Data is either copied from the input file or decompressed from the input
file depending on state->how. If state->how is LOOK, then a gzip header is
looked for to determine whether to copy or decompress. Returns -1 on error,
otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the
end of the input file has been reached and all data has been processed. */
static int gz_fetch(state)
gz_statep state;
{
z_streamp strm = &(state->strm);
do {
switch(state->how) {
case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */
if (gz_look(state) == -1)
return -1;
if (state->how == LOOK)
return 0;
break;
case COPY: /* -> COPY */
if (gz_load(state, state->out, state->size << 1, &(state->x.have))
== -1)
return -1;
state->x.next = state->out;
return 0;
case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */
strm->avail_out = state->size << 1;
strm->next_out = state->out;
if (gz_decomp(state) == -1)
return -1;
}
} while (state->x.have == 0 && (!state->eof || strm->avail_in));
return 0;
}
/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */
static int gz_skip(state, len)
gz_statep state;
int64_t len;
{
unsigned n;
/* skip over len bytes or reach end-of-file, whichever comes first */
while (len)
/* skip over whatever is in output buffer */
if (state->x.have) {
n = GT_OFF(state->x.have) || (int64_t)state->x.have > len ?
(unsigned)len : state->x.have;
state->x.have -= n;
state->x.next += n;
state->x.pos += n;
len -= n;
}
/* output buffer empty -- return if we're at the end of the input */
else if (state->eof && state->strm.avail_in == 0)
break;
/* need more data to skip -- load up output buffer */
else {
/* get more output, looking for header if required */
if (gz_fetch(state) == -1)
return -1;
}
return 0;
}
/* Read len bytes into buf from file, or less than len up to the end of the
input. Return the number of bytes read. If zero is returned, either the
end of file was reached, or there was an error. state->err must be
consulted in that case to determine which. */
static size_t gz_read(state, buf, len)
gz_statep state;
voidp buf;
size_t len;
{
size_t got;
unsigned n;
/* if len is zero, avoid unnecessary operations */
if (len == 0)
return 0;
/* process a skip request */
if (state->seek) {
state->seek = 0;
if (gz_skip(state, state->skip) == -1)
return 0;
}
/* get len bytes to buf, or less than len if at the end */
got = 0;
do {
/* set n to the maximum amount of len that fits in an unsigned int */
n = -1;
if (n > len)
n = len;
/* first just try copying data from the output buffer */
if (state->x.have) {
if (state->x.have < n)
n = state->x.have;
memcpy(buf, state->x.next, n);
state->x.next += n;
state->x.have -= n;
}
/* output buffer empty -- return if we're at the end of the input */
else if (state->eof && state->strm.avail_in == 0) {
state->past = 1; /* tried to read past end */
break;
}
/* need output data -- for small len or new stream load up our output
buffer */
else if (state->how == LOOK || n < (state->size << 1)) {
/* get more output, looking for header if required */
if (gz_fetch(state) == -1)
return 0;
continue; /* no progress yet -- go back to copy above */
/* the copy above assures that we will leave with space in the
output buffer, allowing at least one gzungetc() to succeed */
}
/* large len -- read directly into user buffer */
else if (state->how == COPY) { /* read directly */
if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
return 0;
}
/* large len -- decompress directly into user buffer */
else { /* state->how == GZIP */
state->strm.avail_out = n;
state->strm.next_out = (unsigned char *)buf;
if (gz_decomp(state) == -1)
return 0;
n = state->x.have;
state->x.have = 0;
}
/* update progress */
len -= n;
buf = (char *)buf + n;
got += n;
state->x.pos += n;
} while (len);
/* return number of bytes read into user buffer */
return got;
}
/* -- see zlib.h -- */
int gzread(file, buf, len)
gzFile file;
voidp buf;
unsigned len;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* since an int is returned, make sure len fits in one, otherwise return
with an error (this avoids a flaw in the interface) */
if ((int)len < 0) {
gz_error(state, Z_STREAM_ERROR, "request does not fit in an int");
return -1;
}
/* read len or fewer bytes to buf */
len = gz_read(state, buf, len);
/* check for an error */
if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
return -1;
/* return the number of bytes read (this is assured to fit in an int) */
return (int)len;
}
/* -- see zlib.h -- */
size_t gzfread(buf, size, nitems, file)
voidp buf;
size_t size;
size_t nitems;
gzFile file;
{
size_t len;
gz_statep state;
/* get internal structure */
if (file == NULL)
return 0;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return 0;
/* compute bytes to read -- error on overflow */
len = nitems * size;
if (size && len / size != nitems) {
gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
return 0;
}
/* read len or fewer bytes to buf, return the number of full items read */
return len ? gz_read(state, buf, len) / size : 0;
}
/* -- see zlib.h -- */
#ifdef Z_PREFIX_SET
# undef z_gzgetc
#else
# undef gzgetc
# ifdef Z_CR_PREFIX_SET
# define gzgetc Cr_z_gzgetc
# endif
#endif
int gzgetc(file)
gzFile file;
{
int ret;
unsigned char buf[1];
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* try output buffer (no need to check for skip request) */
if (state->x.have) {
state->x.have--;
state->x.pos++;
return *(state->x.next)++;
}
/* nothing there -- try gz_read() */
ret = gz_read(state, buf, 1);
return ret < 1 ? -1 : buf[0];
}
int gzgetc_(file)
gzFile file;
{
return gzgetc(file);
}
/* -- see zlib.h -- */
int gzungetc(c, file)
int c;
gzFile file;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* process a skip request */
if (state->seek) {
state->seek = 0;
if (gz_skip(state, state->skip) == -1)
return -1;
}
/* can't push EOF */
if (c < 0)
return -1;
/* if output buffer empty, put byte at end (allows more pushing) */
if (state->x.have == 0) {
state->x.have = 1;
state->x.next = state->out + (state->size << 1) - 1;
state->x.next[0] = (unsigned char)c;
state->x.pos--;
state->past = 0;
return c;
}
/* if no room, give up (must have already done a gzungetc()) */
if (state->x.have == (state->size << 1)) {
gz_error(state, Z_DATA_ERROR, "out of room to push characters");
return -1;
}
/* slide output data if needed and insert byte before existing data */
if (state->x.next == state->out) {
unsigned char *src = state->out + state->x.have;
unsigned char *dest = state->out + (state->size << 1);
while (src > state->out)
*--dest = *--src;
state->x.next = dest;
}
state->x.have++;
state->x.next--;
state->x.next[0] = (unsigned char)c;
state->x.pos--;
state->past = 0;
return c;
}
/* -- see zlib.h -- */
char * gzgets(file, buf, len)
gzFile file;
char *buf;
int len;
{
unsigned left, n;
char *str;
unsigned char *eol;
gz_statep state;
/* check parameters and get internal structure */
if (file == NULL || buf == NULL || len < 1)
return NULL;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return NULL;
/* process a skip request */
if (state->seek) {
state->seek = 0;
if (gz_skip(state, state->skip) == -1)
return NULL;
}
/* copy output bytes up to new line or len - 1, whichever comes first --
append a terminating zero to the string (we don't check for a zero in
the contents, let the user worry about that) */
str = buf;
left = (unsigned)len - 1;
if (left) do {
/* assure that something is in the output buffer */
if (state->x.have == 0 && gz_fetch(state) == -1)
return NULL; /* error */
if (state->x.have == 0) { /* end of file */
state->past = 1; /* read past end */
break; /* return what we have */
}
/* look for end-of-line in current output buffer */
n = state->x.have > left ? left : state->x.have;
eol = (unsigned char *)memchr(state->x.next, '\n', n);
if (eol != NULL)
n = (unsigned)(eol - state->x.next) + 1;
/* copy through end-of-line, or remainder if not found */
memcpy(buf, state->x.next, n);
state->x.have -= n;
state->x.next += n;
state->x.pos += n;
left -= n;
buf += n;
} while (left && eol == NULL);
/* return terminated string, or if nothing, end of file */
if (buf == str)
return NULL;
buf[0] = 0;
return str;
}
/* -- see zlib.h -- */
int gzdirect(file)
gzFile file;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return 0;
state = (gz_statep)file;
/* if the state is not known, but we can find out, then do so (this is
mainly for right after a gzopen() or gzdopen()) */
if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
(void)gz_look(state);
/* return 1 if transparent, 0 if processing a gzip stream */
return state->direct;
}
/* -- see zlib.h -- */
int gzclose_r(file)
gzFile file;
{
int ret, err;
gz_statep state;
/* get internal structure */
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
/* check that we're reading */
if (state->mode != GZ_READ)
return Z_STREAM_ERROR;
/* free memory and close file */
if (state->size) {
inflateEnd(&(state->strm));
free(state->out);
free(state->in);
}
err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK;
gz_error(state, Z_OK, NULL);
free(state->path);
ret = close(state->fd);
free(state);
return ret ? Z_ERRNO : err;
}

565
third_party/zlib/gz/gzwrite.c vendored Normal file
View file

@ -0,0 +1,565 @@
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "third_party/zlib/gz/gzguts.inc"
// clang-format off
/* gzwrite.c -- zlib functions for writing gzip files
* Copyright (C) 2004-2017 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
asm(".ident\t\"\\n\\n\
zlib (zlib License)\\n\
Copyright 1995-2017 Jean-loup Gailly and Mark Adler\"");
asm(".include \"libc/disclaimer.inc\"");
/* Static functions */
static int gz_init(gz_statep);
static int gz_comp(gz_statep, int);
static int gz_zero(gz_statep, int64_t);
static size_t gz_write(gz_statep, voidpc, size_t);
/* Initialize state for writing a gzip file. Mark initialization by setting
state->size to non-zero. Return -1 on a memory allocation failure, or 0 on
success. */
static int gz_init(state)
gz_statep state;
{
int ret;
z_streamp strm = &(state->strm);
/* allocate input buffer (double size for gzprintf) */
state->in = (unsigned char *)malloc(state->want << 1);
if (state->in == NULL) {
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
/* only need output buffer and deflate state if compressing */
if (!state->direct) {
/* allocate output buffer */
state->out = (unsigned char *)malloc(state->want);
if (state->out == NULL) {
free(state->in);
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
/* allocate deflate memory, set up for gzip compression */
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
ret = deflateInit2(strm, state->level, Z_DEFLATED,
MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
if (ret != Z_OK) {
free(state->out);
free(state->in);
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
strm->next_in = NULL;
}
/* mark state as initialized */
state->size = state->want;
/* initialize write buffer if compressing */
if (!state->direct) {
strm->avail_out = state->size;
strm->next_out = state->out;
state->x.next = strm->next_out;
}
return 0;
}
/* Compress whatever is at avail_in and next_in and write to the output file.
Return -1 if there is an error writing to the output file or if gz_init()
fails to allocate memory, otherwise 0. flush is assumed to be a valid
deflate() flush value. If flush is Z_FINISH, then the deflate() state is
reset to start a new gzip stream. If gz->direct is true, then simply write
to the output file without compressing, and ignore flush. */
static int gz_comp(state, flush)
gz_statep state;
int flush;
{
int ret, writ;
unsigned have, put, max = ((unsigned)-1 >> 2) + 1;
z_streamp strm = &(state->strm);
/* allocate memory if this is the first time through */
if (state->size == 0 && gz_init(state) == -1)
return -1;
/* write directly if requested */
if (state->direct) {
while (strm->avail_in) {
put = strm->avail_in > max ? max : strm->avail_in;
writ = write(state->fd, strm->next_in, put);
if (writ < 0) {
gz_error(state, Z_ERRNO, zstrerror());
return -1;
}
strm->avail_in -= (unsigned)writ;
strm->next_in += writ;
}
return 0;
}
/* run deflate() on provided input until it produces no more output */
ret = Z_OK;
do {
/* write out current buffer contents if full, or if flushing, but if
doing Z_FINISH then don't write until we get to Z_STREAM_END */
if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
(flush != Z_FINISH || ret == Z_STREAM_END))) {
while (strm->next_out > state->x.next) {
put = strm->next_out - state->x.next > (int)max ? max :
(unsigned)(strm->next_out - state->x.next);
writ = write(state->fd, state->x.next, put);
if (writ < 0) {
gz_error(state, Z_ERRNO, zstrerror());
return -1;
}
state->x.next += writ;
}
if (strm->avail_out == 0) {
strm->avail_out = state->size;
strm->next_out = state->out;
state->x.next = state->out;
}
}
/* compress */
have = strm->avail_out;
ret = deflate(strm, flush);
if (ret == Z_STREAM_ERROR) {
gz_error(state, Z_STREAM_ERROR,
"internal error: deflate stream corrupt");
return -1;
}
have -= strm->avail_out;
} while (have);
/* if that completed a deflate stream, allow another to start */
if (flush == Z_FINISH)
deflateReset(strm);
/* all done, no errors */
return 0;
}
/* Compress len zeros to output. Return -1 on a write error or memory
allocation failure by gz_comp(), or 0 on success. */
static int gz_zero(state, len)
gz_statep state;
int64_t len;
{
int first;
unsigned n;
z_streamp strm = &(state->strm);
/* consume whatever's left in the input buffer */
if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
return -1;
/* compress len zeros (len guaranteed > 0) */
first = 1;
while (len) {
n = GT_OFF(state->size) || (int64_t)state->size > len ?
(unsigned)len : state->size;
if (first) {
memset(state->in, 0, n);
first = 0;
}
strm->avail_in = n;
strm->next_in = state->in;
state->x.pos += n;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return -1;
len -= n;
}
return 0;
}
/* Write len bytes from buf to file. Return the number of bytes written. If
the returned value is less than len, then there was an error. */
static size_t gz_write(state, buf, len)
gz_statep state;
voidpc buf;
size_t len;
{
size_t put = len;
/* if len is zero, avoid unnecessary operations */
if (len == 0)
return 0;
/* allocate memory if this is the first time through */
if (state->size == 0 && gz_init(state) == -1)
return 0;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
return 0;
}
/* for small len, copy to input buffer, otherwise compress directly */
if (len < state->size) {
/* copy to input buffer, compress when full */
do {
unsigned have, copy;
if (state->strm.avail_in == 0)
state->strm.next_in = state->in;
have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
state->in);
copy = state->size - have;
if (copy > len)
copy = len;
memcpy(state->in + have, buf, copy);
state->strm.avail_in += copy;
state->x.pos += copy;
buf = (const char *)buf + copy;
len -= copy;
if (len && gz_comp(state, Z_NO_FLUSH) == -1)
return 0;
} while (len);
}
else {
/* consume whatever's left in the input buffer */
if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
return 0;
/* directly compress user buffer to file */
state->strm.next_in = (const Bytef *)buf;
do {
unsigned n = (unsigned)-1;
if (n > len)
n = len;
state->strm.avail_in = n;
state->x.pos += n;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return 0;
len -= n;
} while (len);
}
/* input was all buffered or compressed */
return put;
}
/* -- see zlib.h -- */
int gzwrite(file, buf, len)
gzFile file;
voidpc buf;
unsigned len;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return 0;
state = (gz_statep)file;
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return 0;
/* since an int is returned, make sure len fits in one, otherwise return
with an error (this avoids a flaw in the interface) */
if ((int)len < 0) {
gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
return 0;
}
/* write len bytes from buf (the return value will fit in an int) */
return (int)gz_write(state, buf, len);
}
/* -- see zlib.h -- */
size_t gzfwrite(buf, size, nitems, file)
voidpc buf;
size_t size;
size_t nitems;
gzFile file;
{
size_t len;
gz_statep state;
/* get internal structure */
if (file == NULL)
return 0;
state = (gz_statep)file;
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return 0;
/* compute bytes to read -- error on overflow */
len = nitems * size;
if (size && len / size != nitems) {
gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
return 0;
}
/* write len bytes to buf, return the number of full items written */
return len ? gz_write(state, buf, len) / size : 0;
}
/* -- see zlib.h -- */
int gzputc(file, c)
gzFile file;
int c;
{
unsigned have;
unsigned char buf[1];
gz_statep state;
z_streamp strm;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
strm = &(state->strm);
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return -1;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
return -1;
}
/* try writing to input buffer for speed (state->size == 0 if buffer not
initialized) */
if (state->size) {
if (strm->avail_in == 0)
strm->next_in = state->in;
have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
if (have < state->size) {
state->in[have] = (unsigned char)c;
strm->avail_in++;
state->x.pos++;
return c & 0xff;
}
}
/* no room in buffer or not initialized, use gz_write() */
buf[0] = (unsigned char)c;
if (gz_write(state, buf, 1) != 1)
return -1;
return c & 0xff;
}
/* -- see zlib.h -- */
int gzputs(file, str)
gzFile file;
const char *str;
{
int ret;
size_t len;
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return -1;
/* write string */
len = strlen(str);
ret = gz_write(state, str, len);
return ret == 0 && len != 0 ? -1 : ret;
}
/* -- see zlib.h -- */
int gzvprintf(gzFile file, const char *format, va_list va)
{
int len;
unsigned left;
char *next;
gz_statep state;
z_streamp strm;
/* get internal structure */
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
strm = &(state->strm);
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return Z_STREAM_ERROR;
/* make sure we have some buffer space */
if (state->size == 0 && gz_init(state) == -1)
return state->err;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
return state->err;
}
/* do the printf() into the input buffer, put length in len -- the input
buffer is double-sized just for this function, so there is guaranteed to
be state->size bytes available after the current contents */
if (strm->avail_in == 0)
strm->next_in = state->in;
next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
next[state->size - 1] = 0;
len = (vsnprintf)(next, state->size, format, va);
/* check that printf() results fit in buffer */
if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
return 0;
/* update buffer and position, compress first half if past that */
strm->avail_in += (unsigned)len;
state->x.pos += len;
if (strm->avail_in >= state->size) {
left = strm->avail_in - state->size;
strm->avail_in = state->size;
if (gz_comp(state, Z_NO_FLUSH) == -1)
return state->err;
memcpy(state->in, state->in + state->size, left);
strm->next_in = state->in;
strm->avail_in = left;
}
return len;
}
int gzprintf(gzFile file, const char *format, ...)
{
va_list va;
int ret;
va_start(va, format);
ret = gzvprintf(file, format, va);
va_end(va);
return ret;
}
/* -- see zlib.h -- */
int gzflush(file, flush)
gzFile file;
int flush;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return Z_STREAM_ERROR;
/* check flush parameter */
if (flush < 0 || flush > Z_FINISH)
return Z_STREAM_ERROR;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
return state->err;
}
/* compress remaining data with requested flush */
(void)gz_comp(state, flush);
return state->err;
}
/* -- see zlib.h -- */
int gzsetparams(file, level, strategy)
gzFile file;
int level;
int strategy;
{
gz_statep state;
z_streamp strm;
/* get internal structure */
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
strm = &(state->strm);
/* check that we're writing and that there's no error */
if (state->mode != GZ_WRITE || state->err != Z_OK)
return Z_STREAM_ERROR;
/* if no change is requested, then do nothing */
if (level == state->level && strategy == state->strategy)
return Z_OK;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
return state->err;
}
/* change compression parameters for subsequent input */
if (state->size) {
/* flush previous input with previous parameters before changing */
if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
return state->err;
deflateParams(strm, level, strategy);
}
state->level = level;
state->strategy = strategy;
return Z_OK;
}
/* -- see zlib.h -- */
int gzclose_w(file)
gzFile file;
{
int ret = Z_OK;
gz_statep state;
/* get internal structure */
if (file == NULL)
return Z_STREAM_ERROR;
state = (gz_statep)file;
/* check that we're writing */
if (state->mode != GZ_WRITE)
return Z_STREAM_ERROR;
/* check for seek request */
if (state->seek) {
state->seek = 0;
if (gz_zero(state, state->skip) == -1)
ret = state->err;
}
/* flush, free memory, and close file */
if (gz_comp(state, Z_FINISH) == -1)
ret = state->err;
if (state->size) {
if (!state->direct) {
(void)deflateEnd(&(state->strm));
free(state->out);
}
free(state->in);
}
gz_error(state, Z_OK, NULL);
free(state->path);
if (close(state->fd) == -1)
ret = Z_ERRNO;
free(state);
return ret;
}

View file

@ -69,5 +69,6 @@ $(THIRD_PARTY_ZLIB_OBJS): $(BUILD_FILES) third_party/zlib/zlib.mk
.PHONY: o/$(MODE)/third_party/zlib
o/$(MODE)/third_party/zlib: \
o/$(MODE)/third_party/zlib/gz \
$(THIRD_PARTY_ZLIB_A) \
$(THIRD_PARTY_ZLIB_CHECKS)

View file

@ -16,19 +16,13 @@ TOOL_ARGS_A_CHECKS = \
$(TOOL_ARGS_A).pkg
TOOL_ARGS_A_DIRECTDEPS = \
LIBC_CALLS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_STUBS \
LIBC_X \
LIBC_ZIPOS \
NET_HTTPS \
THIRD_PARTY_COMPILER_RT
LIBC_ZIPOS
TOOL_ARGS_A_DEPS := \
$(call uniq,$(foreach x,$(TOOL_ARGS_A_DIRECTDEPS),$($(x))))