mirror of
https://github.com/GerbilSoft/zlib-ng.git
synced 2025-06-18 11:35:35 -04:00
168 lines
4.4 KiB
C
168 lines
4.4 KiB
C
/* Compresses a user-specified number of chunks from stdin into stdout as a single gzip stream.
|
|
* Each chunk is compressed with a user-specified level.
|
|
*/
|
|
|
|
#include "zbuild.h"
|
|
#ifdef ZLIB_COMPAT
|
|
# include "zlib.h"
|
|
#else
|
|
# include "zlib-ng.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
# include <fcntl.h>
|
|
# include <io.h>
|
|
# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
|
|
#else
|
|
# define SET_BINARY_MODE(file)
|
|
#endif
|
|
|
|
static int read_all(unsigned char *buf, size_t size) {
|
|
size_t total_read = 0;
|
|
while (total_read < size) {
|
|
size_t n_read = fread(buf + total_read, 1, size - total_read, stdin);
|
|
if (ferror(stdin)) {
|
|
perror("fread\n");
|
|
return 1;
|
|
}
|
|
if (n_read == 0) {
|
|
fprintf(stderr, "Premature EOF\n");
|
|
return 1;
|
|
}
|
|
total_read += n_read;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int write_all(unsigned char *buf, size_t size) {
|
|
size_t total_written = 0;
|
|
while (total_written < size) {
|
|
size_t n_written = fwrite(buf + total_written, 1, size - total_written, stdout);
|
|
if (ferror(stdout)) {
|
|
perror("fwrite\n");
|
|
return 1;
|
|
}
|
|
total_written += n_written;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int compress_chunk(PREFIX3(stream) *strm, int level, int size, int last) {
|
|
int ret = 1;
|
|
int err = 0;
|
|
unsigned long compsize;
|
|
unsigned char *buf;
|
|
|
|
if (size <= 0) {
|
|
fprintf(stderr, "compress_chunk() invalid size %d\n", size);
|
|
goto done;
|
|
}
|
|
if (level < 0 || level > 9) {
|
|
fprintf(stderr, "compress_chunk() invalid level %d\n", level);
|
|
goto done;
|
|
}
|
|
|
|
compsize = PREFIX(deflateBound)(strm, size);
|
|
buf = malloc(size + compsize);
|
|
if (buf == NULL) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
goto done;
|
|
}
|
|
if (read_all(buf, size) != 0) {
|
|
goto free_buf;
|
|
}
|
|
|
|
/* Provide only output buffer to deflateParams(). It might need some space to flush the leftovers from the last
|
|
* deflate(), but we don't want it to compress anything new. */
|
|
strm->next_in = NULL;
|
|
strm->avail_in = 0;
|
|
strm->next_out = buf + size;
|
|
strm->avail_out = compsize;
|
|
err = PREFIX(deflateParams)(strm, level, Z_DEFAULT_STRATEGY);
|
|
if (err != Z_OK) {
|
|
fprintf(stderr, "deflateParams() failed with code %d\n", err);
|
|
goto free_buf;
|
|
}
|
|
|
|
/* Provide input buffer to deflate(). */
|
|
strm->next_in = buf;
|
|
strm->avail_in = size;
|
|
err = PREFIX(deflate)(strm, last ? Z_FINISH : Z_SYNC_FLUSH);
|
|
if ((!last && err != Z_OK) || (last && err != Z_STREAM_END)) {
|
|
fprintf(stderr, "deflate() failed with code %d\n", err);
|
|
goto free_buf;
|
|
}
|
|
if (strm->avail_in != 0) {
|
|
fprintf(stderr, "deflate() did not consume %d bytes of input\n", strm->avail_in);
|
|
goto free_buf;
|
|
}
|
|
if (write_all(buf + size, compsize - strm->avail_out) != 0) {
|
|
goto free_buf;
|
|
}
|
|
ret = 0;
|
|
|
|
free_buf:
|
|
free(buf);
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
void show_help(void)
|
|
{
|
|
printf("Usage: switchlevels [-w bits] level1 size1 [level2 size2 ...]\n\n"
|
|
" -w : window bits (8 to 15 for gzip, -8 to -15 for zlib)\n\n");
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int ret = EXIT_FAILURE;
|
|
int err = 0;
|
|
int size = 0;
|
|
int level = Z_DEFAULT_COMPRESSION;
|
|
int level_arg = 1;
|
|
int window_bits = MAX_WBITS + 16;
|
|
PREFIX3(stream) strm;
|
|
|
|
|
|
if ((argc == 1) || (argc == 2 && strcmp(argv[1], "--help") == 0)) {
|
|
show_help();
|
|
return 0;
|
|
}
|
|
|
|
SET_BINARY_MODE(stdin);
|
|
SET_BINARY_MODE(stdout);
|
|
|
|
memset(&strm, 0, sizeof(strm));
|
|
|
|
for (int i = 1; i < argc - 1; i++) {
|
|
if (strcmp(argv[i], "-w") == 0 && i+1 < argc) {
|
|
window_bits = atoi(argv[++i]);
|
|
} else {
|
|
level_arg = i;
|
|
level = atoi(argv[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
err = PREFIX(deflateInit2)(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
|
|
if (err != Z_OK) {
|
|
fprintf(stderr, "deflateInit() failed with code %d\n", err);
|
|
goto done;
|
|
}
|
|
|
|
for (int i = level_arg; i < argc - 1; i += 2) {
|
|
level = atoi(argv[i]);
|
|
size = atoi(argv[i + 1]);
|
|
if (compress_chunk(&strm, level, size, i + 2 >= argc - 1) != 0) {
|
|
goto deflate_end;
|
|
}
|
|
}
|
|
ret = EXIT_SUCCESS;
|
|
|
|
deflate_end:
|
|
PREFIX(deflateEnd)(&strm);
|
|
done:
|
|
return ret;
|
|
}
|