From de3e5ea4fe9c42f27d442aa87b7d16e090e72485 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 26 Apr 2017 15:01:41 -0700 Subject: Implement zlib-based gzip/gunzip/zcat. --- Config.in | 6 ++ scripts/make.sh | 2 +- toys/pending/compress.c | 93 ---------------------- toys/pending/gzip.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 94 deletions(-) create mode 100644 toys/pending/gzip.c diff --git a/Config.in b/Config.in index 2bfe30a8..0524ce37 100644 --- a/Config.in +++ b/Config.in @@ -77,6 +77,12 @@ config TOYBOX_LIBCRYPTO help Use faster hash functions out of exteral -lcrypto library. +config TOYBOX_LIBZ + bool "Use libz (zlib)" + default n + help + Use libz for gz support. + config TOYBOX_FLOAT bool "Floating point support" default y diff --git a/scripts/make.sh b/scripts/make.sh index 57928471..2d33be4e 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -109,7 +109,7 @@ then # for it. > generated/optlibs.dat - for i in util crypt m resolv selinux smack attr rt crypto + for i in util crypt m resolv selinux smack attr rt crypto z do echo "int main(int argc, char *argv[]) {return 0;}" | \ ${CROSS_COMPILE}${CC} $CFLAGS -xc - -o generated/libprobe -Wl,--as-needed -l$i > /dev/null 2>/dev/null && diff --git a/toys/pending/compress.c b/toys/pending/compress.c index 1749ee4f..5605a354 100644 --- a/toys/pending/compress.c +++ b/toys/pending/compress.c @@ -16,9 +16,6 @@ // Leave Lrg at end so flag values line up. USE_COMPRESS(NEWTOY(compress, "zcd9lrg[-cd][!zgLr]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_GZIP(NEWTOY(gzip, USE_GZIP_D("d")"19dcflqStvgLRz[!gLRz]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_ZCAT(NEWTOY(zcat, 0, TOYFLAG_USR|TOYFLAG_BIN)) -USE_GUNZIP(NEWTOY(gunzip, "cflqStv", TOYFLAG_USR|TOYFLAG_BIN)) //zip unzip gzip gunzip zcat @@ -37,40 +34,6 @@ config COMPRESS -R raw -z zip -config GZIP - bool "gzip" - default y - depends on COMPRESS - help - usage: gzip [-19cfqStvzgLR] [FILE...] - - Compess (deflate) file(s). With no files, compress stdin to stdout. - - On successful decompression, compressed files are replaced with the - uncompressed version. The input file is removed and replaced with - a new file without the .gz extension (with same ownership/permissions). - - -1 Minimal compression (fastest) - -9 Max compression (default) - -c cat to stdout (act as zcat) - -f force (if output file exists, input is tty, unrecognized extension) - -q quiet (no warnings) - -S specify exension (default .*) - -t test compressed file(s) - -v verbose (like -l, but compress files) - - Compression type: - -g gzip (default) -L zlib -R raw -z zip - -config GZIP_D - bool - default y - depends on GZIP && DECOMPRESS - help - usage: gzip [-d] - - -d decompress (act as gunzip) - config DECOMPRESS bool "decompress" default n @@ -81,37 +44,6 @@ config DECOMPRESS -c compress with -g gzip (default) -l zlib -r raw -z zip -d decompress (autodetects type) - - -config ZCAT - bool "zcat" - default y - depends on DECOMPRESS - help - usage: zcat [FILE...] - - Decompress deflated file(s) to stdout - -config GUNZIP - bool "gunzip" - default y - depends on DECOMPRESS - help - usage: gunzip [-cflqStv] [FILE...] - - Decompess (deflate) file(s). With no files, compress stdin to stdout. - - On successful decompression, compressed files are replaced with the - uncompressed version. The input file is removed and replaced with - a new file without the .gz extension (with same ownership/permissions). - - -c cat to stdout (act as zcat) - -f force (output file exists, input is tty, unrecognized extension) - -l list compressed/uncompressed/ratio/name for each input file. - -q quiet (no warnings) - -S specify exension (default .*) - -t test compressed file(s) - -v verbose (like -l, but decompress files) */ #define FOR_compress @@ -579,28 +511,3 @@ void compress_main(void) // todo: this printf("hello world"); } - -//#define CLEANUP_compress -//#define FOR_zcat -//#include "generated/flags.h" - -void zcat_main(void) -{ - init_deflate(0); - - loopfiles(toys.optargs, do_zcat); -} - -void gunzip_main(void) -{ - init_deflate(0); - - loopfiles(toys.optargs, do_zcat); -} - -void gzip_main(void) -{ - init_deflate(1); - - loopfiles(toys.optargs, do_gzip); -} diff --git a/toys/pending/gzip.c b/toys/pending/gzip.c new file mode 100644 index 00000000..f7576c66 --- /dev/null +++ b/toys/pending/gzip.c @@ -0,0 +1,199 @@ +/* gzip.c - gzip/gunzip/zcat + * + * Copyright 2017 The Android Open Source Project + * + * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt + +// Existing implementations allow all options for all commands. +USE_GZIP(NEWTOY(gzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN)) +USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN)) +USE_ZCAT(NEWTOY(zcat, "cdfk123456789", TOYFLAG_USR|TOYFLAG_BIN)) + +config GZIP + bool "gzip" + default y + depends on TOYBOX_LIBZ + help + usage: gzip [-19cdfk] [FILE...] + + Compress files. With no files, compresses stdin to stdout. + On success, the input files are removed and replaced by new + files with the .gz suffix. + + -c Output to stdout + -d Decompress (act as gunzip) + -f Force: allow overwrite of output file + -k Keep input files (default is to remove) + -# Compression level 1-9 (1:fastest, 6:default, 9:best) + +config GUNZIP + bool "gunzip" + default y + depends on TOYBOX_LIBZ + help + usage: gunzip [-cfk] [FILE...] + + Decompress files. With no files, decompresses stdin to stdout. + On success, the input files are removed and replaced by new + files without the .gz suffix. + + -c Output to stdout (act as zcat) + -f Force: allow read from tty + -k Keep input files (default is to remove) + +config ZCAT + bool "zcat" + default y + depends on TOYBOX_LIBZ + help + usage: zcat [FILE...] + + Decompress files to stdout. Like `gzip -dc`. + + -c Output to stdout (default) + -f Force: allow read from tty +*/ + +#include + +#define FORCE_FLAGS +#define FOR_gzip +#include "toys.h" + +GLOBALS( + int level; +) + +static void fix_time(const char *path, struct stat *sb) +{ + struct timespec times[] = { sb->st_atim, sb->st_mtim }; + + if (utimensat(AT_FDCWD, path, times, 0)) perror_exit("utimensat"); +} + +static void gzerror_exit(gzFile f, char *what) +{ + int err; + const char *msg = gzerror(f, &err); + + ((err == Z_ERRNO) ? perror_exit : error_exit)("%s: %s", what, msg); +} + +static void do_gunzip(int in_fd, char *arg) +{ + struct stat sb; + int len, both_files; + char *in_name, *out_name; + gzFile in; + FILE *out; + + // "gunzip x.gz" will decompress "x.gz" to "x". + len = strlen(arg); + if (len > 3 && !strcmp(arg+len-3, ".gz")) { + in_name = strdup(arg); + out_name = strdup(arg); + out_name[len-3] = '\0'; + } else if (!strcmp(arg, "-")) { + // "-" means stdin; assume output to stdout. + // TODO: require -f to read compressed data from tty? + in_name = strdup("-"); + out_name = strdup("-"); + } else error_exit("unknown suffix: %s", arg); + + if (toys.optflags&FLAG_c) { + free(out_name); + out_name = strdup("-"); + } + + both_files = strcmp(in_name, "-") && strcmp(out_name, "-"); + if (both_files) xstat(in_name, &sb); + + in = gzdopen(in_fd, "r"); + if (in == NULL) perror_exit("gzdopen"); + if (!strcmp(out_name, "-")) out = stdout; + else { + int out_fd = xcreate(out_name, + O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL), + both_files?sb.st_mode:0666); + + out = xfdopen(out_fd, "w"); + } + + while ((len = gzread(in, toybuf, sizeof(toybuf))) > 0) { + if (fwrite(toybuf, 1, len, out) != (size_t) len) perror_exit("writing"); + } + if (len < 0) gzerror_exit(in, "gzread"); + if (out != stdout && fclose(out)) perror_exit("writing"); + if (gzclose(in) != Z_OK) error_exit("gzclose"); + + if (both_files) fix_time(out_name, &sb); + if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name); + free(in_name); + free(out_name); +} + +static void do_gzip(int in_fd, char *in_name) +{ + size_t len; + char *out_name; + FILE *in = xfdopen(in_fd, "r"); + gzFile out; + struct stat sb; + int both_files, out_fd; + + out_name = (toys.optflags&FLAG_c) ? strdup("-") : xmprintf("%s.gz", in_name); + both_files = strcmp(in_name, "-") && strcmp(out_name, "-"); + if (both_files) xstat(in_name, &sb); + + if (!strcmp(out_name, "-")) out_fd = dup(1); + else { + out_fd = open(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL), + both_files?sb.st_mode:0); + } + if (out_fd == -1) perror_exit("open %s", out_name); + + snprintf(toybuf, sizeof(toybuf), "w%d", TT.level); + out = gzdopen(out_fd, toybuf); + if (out == NULL) perror_exit("gzdopen %s", out_name); + + while ((len = fread(toybuf, 1, sizeof(toybuf), in)) > 0) { + if (gzwrite(out, toybuf, len) != (int) len) gzerror_exit(out, "gzwrite"); + } + if (ferror(in)) perror_exit("fread"); + if (fclose(in)) perror_exit("fclose"); + if (gzclose(out) != Z_OK) error_exit("gzclose"); + + if (both_files) fix_time(out_name, &sb); + if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name); + free(out_name); +} + +static void do_gz(int fd, char *name) +{ + if (toys.optflags&FLAG_d) do_gunzip(fd, name); + else do_gzip(fd, name); +} + +void gzip_main(void) +{ + int i = (toys.optflags&0x1ff); + + for (TT.level = (i == 0) ? 6 : 10; i; i >>= 1) --TT.level; + + // With no arguments, go from stdin to stdout. + if (!*toys.optargs) toys.optflags |= FLAG_c; + + loopfiles(toys.optargs, do_gz); +} + +void gunzip_main(void) +{ + toys.optflags |= FLAG_d; + gzip_main(); +} + +void zcat_main(void) +{ + toys.optflags |= (FLAG_c|FLAG_d); + gzip_main(); +} -- cgit v1.2.3