alpar@9: /* gzlib.c -- zlib functions common to reading and writing gzip files alpar@9: * Copyright (C) 2004, 2010 Mark Adler alpar@9: * For conditions of distribution and use, see copyright notice in zlib.h alpar@9: */ alpar@9: alpar@9: #include "gzguts.h" alpar@9: alpar@9: #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 alpar@9: # define LSEEK lseek64 alpar@9: #else alpar@9: # define LSEEK lseek alpar@9: #endif alpar@9: alpar@9: /* Local functions */ alpar@9: local void gz_reset OF((gz_statep)); alpar@9: local gzFile gz_open OF((const char *, int, const char *)); alpar@9: alpar@9: #if defined UNDER_CE alpar@9: alpar@9: /* Map the Windows error number in ERROR to a locale-dependent error message alpar@9: string and return a pointer to it. Typically, the values for ERROR come alpar@9: from GetLastError. alpar@9: alpar@9: The string pointed to shall not be modified by the application, but may be alpar@9: overwritten by a subsequent call to gz_strwinerror alpar@9: alpar@9: The gz_strwinerror function does not change the current setting of alpar@9: GetLastError. */ alpar@9: char ZLIB_INTERNAL *gz_strwinerror (error) alpar@9: DWORD error; alpar@9: { alpar@9: static char buf[1024]; alpar@9: alpar@9: wchar_t *msgbuf; alpar@9: DWORD lasterr = GetLastError(); alpar@9: DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM alpar@9: | FORMAT_MESSAGE_ALLOCATE_BUFFER, alpar@9: NULL, alpar@9: error, alpar@9: 0, /* Default language */ alpar@9: (LPVOID)&msgbuf, alpar@9: 0, alpar@9: NULL); alpar@9: if (chars != 0) { alpar@9: /* If there is an \r\n appended, zap it. */ alpar@9: if (chars >= 2 alpar@9: && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { alpar@9: chars -= 2; alpar@9: msgbuf[chars] = 0; alpar@9: } alpar@9: alpar@9: if (chars > sizeof (buf) - 1) { alpar@9: chars = sizeof (buf) - 1; alpar@9: msgbuf[chars] = 0; alpar@9: } alpar@9: alpar@9: wcstombs(buf, msgbuf, chars + 1); alpar@9: LocalFree(msgbuf); alpar@9: } alpar@9: else { alpar@9: sprintf(buf, "unknown win32 error (%ld)", error); alpar@9: } alpar@9: alpar@9: SetLastError(lasterr); alpar@9: return buf; alpar@9: } alpar@9: alpar@9: #endif /* UNDER_CE */ alpar@9: alpar@9: /* Reset gzip file state */ alpar@9: local void gz_reset(state) alpar@9: gz_statep state; alpar@9: { alpar@9: if (state->mode == GZ_READ) { /* for reading ... */ alpar@9: state->have = 0; /* no output data available */ alpar@9: state->eof = 0; /* not at end of file */ alpar@9: state->how = LOOK; /* look for gzip header */ alpar@9: state->direct = 1; /* default for empty file */ alpar@9: } alpar@9: state->seek = 0; /* no seek request pending */ alpar@9: gz_error(state, Z_OK, NULL); /* clear error */ alpar@9: state->pos = 0; /* no uncompressed data yet */ alpar@9: state->strm.avail_in = 0; /* no input data yet */ alpar@9: } alpar@9: alpar@9: /* Open a gzip file either by name or file descriptor. */ alpar@9: local gzFile gz_open(path, fd, mode) alpar@9: const char *path; alpar@9: int fd; alpar@9: const char *mode; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* allocate gzFile structure to return */ alpar@9: state = malloc(sizeof(gz_state)); alpar@9: if (state == NULL) alpar@9: return NULL; alpar@9: state->size = 0; /* no buffers allocated yet */ alpar@9: state->want = GZBUFSIZE; /* requested buffer size */ alpar@9: state->msg = NULL; /* no error message yet */ alpar@9: alpar@9: /* interpret mode */ alpar@9: state->mode = GZ_NONE; alpar@9: state->level = Z_DEFAULT_COMPRESSION; alpar@9: state->strategy = Z_DEFAULT_STRATEGY; alpar@9: while (*mode) { alpar@9: if (*mode >= '0' && *mode <= '9') alpar@9: state->level = *mode - '0'; alpar@9: else alpar@9: switch (*mode) { alpar@9: case 'r': alpar@9: state->mode = GZ_READ; alpar@9: break; alpar@9: #ifndef NO_GZCOMPRESS alpar@9: case 'w': alpar@9: state->mode = GZ_WRITE; alpar@9: break; alpar@9: case 'a': alpar@9: state->mode = GZ_APPEND; alpar@9: break; alpar@9: #endif alpar@9: case '+': /* can't read and write at the same time */ alpar@9: free(state); alpar@9: return NULL; alpar@9: case 'b': /* ignore -- will request binary anyway */ alpar@9: break; alpar@9: case 'f': alpar@9: state->strategy = Z_FILTERED; alpar@9: break; alpar@9: case 'h': alpar@9: state->strategy = Z_HUFFMAN_ONLY; alpar@9: break; alpar@9: case 'R': alpar@9: state->strategy = Z_RLE; alpar@9: break; alpar@9: case 'F': alpar@9: state->strategy = Z_FIXED; alpar@9: default: /* could consider as an error, but just ignore */ alpar@9: ; alpar@9: } alpar@9: mode++; alpar@9: } alpar@9: alpar@9: /* must provide an "r", "w", or "a" */ alpar@9: if (state->mode == GZ_NONE) { alpar@9: free(state); alpar@9: return NULL; alpar@9: } alpar@9: alpar@9: /* save the path name for error messages */ alpar@9: state->path = malloc(strlen(path) + 1); alpar@9: if (state->path == NULL) { alpar@9: free(state); alpar@9: return NULL; alpar@9: } alpar@9: strcpy(state->path, path); alpar@9: alpar@9: /* open the file with the appropriate mode (or just use fd) */ alpar@9: state->fd = fd != -1 ? fd : alpar@9: open(path, alpar@9: #ifdef O_LARGEFILE alpar@9: O_LARGEFILE | alpar@9: #endif alpar@9: #ifdef O_BINARY alpar@9: O_BINARY | alpar@9: #endif alpar@9: (state->mode == GZ_READ ? alpar@9: O_RDONLY : alpar@9: (O_WRONLY | O_CREAT | ( alpar@9: state->mode == GZ_WRITE ? alpar@9: O_TRUNC : alpar@9: O_APPEND))), alpar@9: 0666); alpar@9: if (state->fd == -1) { alpar@9: free(state->path); alpar@9: free(state); alpar@9: return NULL; alpar@9: } alpar@9: if (state->mode == GZ_APPEND) alpar@9: state->mode = GZ_WRITE; /* simplify later checks */ alpar@9: alpar@9: /* save the current position for rewinding (only if reading) */ alpar@9: if (state->mode == GZ_READ) { alpar@9: state->start = LSEEK(state->fd, 0, SEEK_CUR); alpar@9: if (state->start == -1) state->start = 0; alpar@9: } alpar@9: alpar@9: /* initialize stream */ alpar@9: gz_reset(state); alpar@9: alpar@9: /* return stream */ alpar@9: return (gzFile)state; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: gzFile ZEXPORT gzopen(path, mode) alpar@9: const char *path; alpar@9: const char *mode; alpar@9: { alpar@9: return gz_open(path, -1, mode); alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: gzFile ZEXPORT gzopen64(path, mode) alpar@9: const char *path; alpar@9: const char *mode; alpar@9: { alpar@9: return gz_open(path, -1, mode); alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: gzFile ZEXPORT gzdopen(fd, mode) alpar@9: int fd; alpar@9: const char *mode; alpar@9: { alpar@9: char *path; /* identifier for error messages */ alpar@9: gzFile gz; alpar@9: alpar@9: if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) alpar@9: return NULL; alpar@9: sprintf(path, "", fd); /* for debugging */ alpar@9: gz = gz_open(path, fd, mode); alpar@9: free(path); alpar@9: return gz; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: int ZEXPORT gzbuffer(file, size) alpar@9: gzFile file; alpar@9: unsigned size; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return -1; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return -1; alpar@9: alpar@9: /* make sure we haven't already allocated memory */ alpar@9: if (state->size != 0) alpar@9: return -1; alpar@9: alpar@9: /* check and set requested size */ alpar@9: if (size == 0) alpar@9: return -1; alpar@9: state->want = size; alpar@9: return 0; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: int ZEXPORT gzrewind(file) alpar@9: gzFile file; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure */ alpar@9: if (file == NULL) alpar@9: return -1; alpar@9: state = (gz_statep)file; alpar@9: alpar@9: /* check that we're reading and that there's no error */ alpar@9: if (state->mode != GZ_READ || state->err != Z_OK) alpar@9: return -1; alpar@9: alpar@9: /* back up and start over */ alpar@9: if (LSEEK(state->fd, state->start, SEEK_SET) == -1) alpar@9: return -1; alpar@9: gz_reset(state); alpar@9: return 0; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off64_t ZEXPORT gzseek64(file, offset, whence) alpar@9: gzFile file; alpar@9: z_off64_t offset; alpar@9: int whence; alpar@9: { alpar@9: unsigned n; alpar@9: z_off64_t ret; alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return -1; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return -1; alpar@9: alpar@9: /* check that there's no error */ alpar@9: if (state->err != Z_OK) alpar@9: return -1; alpar@9: alpar@9: /* can only seek from start or relative to current position */ alpar@9: if (whence != SEEK_SET && whence != SEEK_CUR) alpar@9: return -1; alpar@9: alpar@9: /* normalize offset to a SEEK_CUR specification */ alpar@9: if (whence == SEEK_SET) alpar@9: offset -= state->pos; alpar@9: else if (state->seek) alpar@9: offset += state->skip; alpar@9: state->seek = 0; alpar@9: alpar@9: /* if within raw area while reading, just go there */ alpar@9: if (state->mode == GZ_READ && state->how == COPY && alpar@9: state->pos + offset >= state->raw) { alpar@9: ret = LSEEK(state->fd, offset - state->have, SEEK_CUR); alpar@9: if (ret == -1) alpar@9: return -1; alpar@9: state->have = 0; alpar@9: state->eof = 0; alpar@9: state->seek = 0; alpar@9: gz_error(state, Z_OK, NULL); alpar@9: state->strm.avail_in = 0; alpar@9: state->pos += offset; alpar@9: return state->pos; alpar@9: } alpar@9: alpar@9: /* calculate skip amount, rewinding if needed for back seek when reading */ alpar@9: if (offset < 0) { alpar@9: if (state->mode != GZ_READ) /* writing -- can't go backwards */ alpar@9: return -1; alpar@9: offset += state->pos; alpar@9: if (offset < 0) /* before start of file! */ alpar@9: return -1; alpar@9: if (gzrewind(file) == -1) /* rewind, then skip to offset */ alpar@9: return -1; alpar@9: } alpar@9: alpar@9: /* if reading, skip what's in output buffer (one less gzgetc() check) */ alpar@9: if (state->mode == GZ_READ) { alpar@9: n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? alpar@9: (unsigned)offset : state->have; alpar@9: state->have -= n; alpar@9: state->next += n; alpar@9: state->pos += n; alpar@9: offset -= n; alpar@9: } alpar@9: alpar@9: /* request skip (if not zero) */ alpar@9: if (offset) { alpar@9: state->seek = 1; alpar@9: state->skip = offset; alpar@9: } alpar@9: return state->pos + offset; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off_t ZEXPORT gzseek(file, offset, whence) alpar@9: gzFile file; alpar@9: z_off_t offset; alpar@9: int whence; alpar@9: { alpar@9: z_off64_t ret; alpar@9: alpar@9: ret = gzseek64(file, (z_off64_t)offset, whence); alpar@9: return ret == (z_off_t)ret ? (z_off_t)ret : -1; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off64_t ZEXPORT gztell64(file) alpar@9: gzFile file; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return -1; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return -1; alpar@9: alpar@9: /* return position */ alpar@9: return state->pos + (state->seek ? state->skip : 0); alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off_t ZEXPORT gztell(file) alpar@9: gzFile file; alpar@9: { alpar@9: z_off64_t ret; alpar@9: alpar@9: ret = gztell64(file); alpar@9: return ret == (z_off_t)ret ? (z_off_t)ret : -1; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off64_t ZEXPORT gzoffset64(file) alpar@9: gzFile file; alpar@9: { alpar@9: z_off64_t offset; alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return -1; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return -1; alpar@9: alpar@9: /* compute and return effective offset in file */ alpar@9: offset = LSEEK(state->fd, 0, SEEK_CUR); alpar@9: if (offset == -1) alpar@9: return -1; alpar@9: if (state->mode == GZ_READ) /* reading */ alpar@9: offset -= state->strm.avail_in; /* don't count buffered input */ alpar@9: return offset; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: z_off_t ZEXPORT gzoffset(file) alpar@9: gzFile file; alpar@9: { alpar@9: z_off64_t ret; alpar@9: alpar@9: ret = gzoffset64(file); alpar@9: return ret == (z_off_t)ret ? (z_off_t)ret : -1; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: int ZEXPORT gzeof(file) alpar@9: gzFile file; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return 0; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return 0; alpar@9: alpar@9: /* return end-of-file state */ alpar@9: return state->mode == GZ_READ ? alpar@9: (state->eof && state->strm.avail_in == 0 && state->have == 0) : 0; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: const char * ZEXPORT gzerror(file, errnum) alpar@9: gzFile file; alpar@9: int *errnum; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return NULL; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return NULL; alpar@9: alpar@9: /* return error information */ alpar@9: if (errnum != NULL) alpar@9: *errnum = state->err; alpar@9: return state->msg == NULL ? "" : state->msg; alpar@9: } alpar@9: alpar@9: /* -- see zlib.h -- */ alpar@9: void ZEXPORT gzclearerr(file) alpar@9: gzFile file; alpar@9: { alpar@9: gz_statep state; alpar@9: alpar@9: /* get internal structure and check integrity */ alpar@9: if (file == NULL) alpar@9: return; alpar@9: state = (gz_statep)file; alpar@9: if (state->mode != GZ_READ && state->mode != GZ_WRITE) alpar@9: return; alpar@9: alpar@9: /* clear error and end-of-file */ alpar@9: if (state->mode == GZ_READ) alpar@9: state->eof = 0; alpar@9: gz_error(state, Z_OK, NULL); alpar@9: } alpar@9: alpar@9: /* Create an error message in allocated memory and set state->err and alpar@9: state->msg accordingly. Free any previous error message already there. Do alpar@9: not try to free or allocate space if the error is Z_MEM_ERROR (out of alpar@9: memory). Simply save the error message as a static string. If there is an alpar@9: allocation failure constructing the error message, then convert the error to alpar@9: out of memory. */ alpar@9: void ZLIB_INTERNAL gz_error(state, err, msg) alpar@9: gz_statep state; alpar@9: int err; alpar@9: const char *msg; alpar@9: { alpar@9: /* free previously allocated message and clear */ alpar@9: if (state->msg != NULL) { alpar@9: if (state->err != Z_MEM_ERROR) alpar@9: free(state->msg); alpar@9: state->msg = NULL; alpar@9: } alpar@9: alpar@9: /* set error code, and if no message, then done */ alpar@9: state->err = err; alpar@9: if (msg == NULL) alpar@9: return; alpar@9: alpar@9: /* for an out of memory error, save as static string */ alpar@9: if (err == Z_MEM_ERROR) { alpar@9: state->msg = (char *)msg; alpar@9: return; alpar@9: } alpar@9: alpar@9: /* construct error message with path */ alpar@9: if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { alpar@9: state->err = Z_MEM_ERROR; alpar@9: state->msg = (char *)"out of memory"; alpar@9: return; alpar@9: } alpar@9: strcpy(state->msg, state->path); alpar@9: strcat(state->msg, ": "); alpar@9: strcat(state->msg, msg); alpar@9: return; alpar@9: } alpar@9: alpar@9: #ifndef INT_MAX alpar@9: /* portably return maximum value for an int (when limits.h presumed not alpar@9: available) -- we need to do this to cover cases where 2's complement not alpar@9: used, since C standard permits 1's complement and sign-bit representations, alpar@9: otherwise we could just use ((unsigned)-1) >> 1 */ alpar@9: unsigned ZLIB_INTERNAL gz_intmax() alpar@9: { alpar@9: unsigned p, q; alpar@9: alpar@9: p = 1; alpar@9: do { alpar@9: q = p; alpar@9: p <<= 1; alpar@9: p++; alpar@9: } while (p > q); alpar@9: return q >> 1; alpar@9: } alpar@9: #endif