123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- /*
- * Authors: Faidon Liambotis <paravoid@debian.org>
- * Josh Triplett <josh@joshtriplett.org>
- *
- * This is a zlib-based gzip that is heavily based on NetBSD's gzip,
- * developed by Matthew R. Green.
- *
- * This is suited for gzip regeneration and is part of pristine-tar.
- * As such, it adds some extra options which are needed to successfully
- * reproduce the gzips out there and removes features of the original
- * implementation that were not relevant (e.g. decompression)
- *
- * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
- * Copyright (c) 2007 Faidon Liambotis
- * Copyright (c) 2008 Josh Triplett
- * Copyright (c) 2010 Joey Hess
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /*
- * gzip.c -- GPL free gzip using zlib.
- *
- * RFC 1950 covers the zlib format
- * RFC 1951 covers the deflate format
- * RFC 1952 covers the gzip format
- *
- */
- #define _GNU_SOURCE
- #include <sys/param.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <inttypes.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <err.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <zlib.h>
- #include <libgen.h>
- #include <stdarg.h>
- #include <getopt.h>
- #include <time.h>
- extern void gnuzip(int in, int out, char *origname, unsigned long timestamp, int level, int osflag, int rsync, int rsync14, int rsync16);
- #define BUFLEN (64 * 1024)
- #define GZIP_MAGIC0 0x1F
- #define GZIP_MAGIC1 0x8B
- #define HEAD_CRC 0x02
- #define EXTRA_FIELD 0x04
- #define ORIG_NAME 0x08
- #define COMMENT 0x10
- #define GZIP_OS_UNIX 3 /* Unix */
- #define GZIP_OS_NTFS 11 /* NTFS */
- static const char gzip_version[] = "zgz 20100613 based on NetBSD gzip 20060927, GNU gzip 1.3.12";
- static const char gzip_copyright[] = \
- " Authors: Faidon Liambotis <paravoid@debian.org>\n"
- " Josh Triplett <josh@joshtriplett.org>\n"
- "\n"
- " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
- " Copyright (c) 2007 Faidon Liambotis\n"
- " Copyright (c) 2008 Josh Triplett\n"
- " * All rights reserved.\n"
- " *\n"
- " * Redistribution and use in source and binary forms, with or without\n"
- " * modification, are permitted provided that the following conditions\n"
- " * are met:\n"
- " * 1. Redistributions of source code must retain the above copyright\n"
- " * notice, this list of conditions and the following disclaimer.\n"
- " * 2. Redistributions in binary form must reproduce the above copyright\n"
- " * notice, this list of conditions and the following disclaimer in the\n"
- " * documentation and/or other materials provided with the distribution.\n"
- " * 3. The name of the author may not be used to endorse or promote products\n"
- " * derived from this software without specific prior written permission.\n"
- " *\n"
- " * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
- " * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
- " * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
- " * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
- " * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
- " * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
- " * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
- " * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
- " * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
- " * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
- " * SUCH DAMAGE.";
- static int qflag; /* quiet mode */
- static void maybe_err(const char *fmt, ...)
- __attribute__((__format__(__printf__, 1, 2),noreturn));
- static void maybe_errx(const char *fmt, ...)
- __attribute__((__format__(__printf__, 1, 2),noreturn));
- static void gz_compress(int, int, const char *, uint32_t, int, int, int, int, int);
- static void usage(void);
- static void display_version(void);
- static void display_license(void);
- int main(int, char **p);
- static const struct option longopts[] = {
- { "stdout", no_argument, 0, 'c' },
- { "to-stdout", no_argument, 0, 'c' },
- { "decompress", no_argument, 0, 'd' },
- { "uncompress", no_argument, 0, 'd' },
- { "force", no_argument, 0, 'f' },
- { "help", no_argument, 0, 'h' },
- { "no-name", no_argument, 0, 'n' },
- { "name", no_argument, 0, 'N' },
- { "quiet", no_argument, 0, 'q' },
- { "fast", no_argument, 0, '1' },
- { "best", no_argument, 0, '9' },
- { "ascii", no_argument, 0, 'a' },
- /* new options */
- { "gnu", no_argument, 0, 'G' },
- { "zlib", no_argument, 0, 'Z' },
- { "rsyncable", no_argument, 0, 'R' },
- { "new-rsyncable", no_argument, 0, 'r' },
- { "16-rsyncable", no_argument, 0, 'D' },
- { "no-timestamp", no_argument, 0, 'm' },
- { "force-timestamp", no_argument, 0, 'M' },
- { "timestamp", required_argument, 0, 'T' },
- { "osflag", required_argument, 0, 's' },
- { "original-name", required_argument, 0, 'o' },
- { "filename", required_argument, 0, 'F' },
- { "quirk", required_argument, 0, 'k' },
- /* end */
- { "version", no_argument, 0, 'V' },
- { "license", no_argument, 0, 'L' },
- { NULL, no_argument, 0, 0 },
- };
- int
- main(int argc, char **argv)
- {
- const char *progname = argv[0];
- int gnu = 0;
- int quirks = 0;
- char *origname = NULL;
- uint32_t timestamp = 0;
- int memlevel = 8; /* zlib's default */
- int nflag = 0;
- int mflag = 0;
- int fflag = 0;
- int xflag = -1;
- int ntfs_quirk = 0;
- int level = 6;
- int osflag = GZIP_OS_UNIX;
- int rsync = 0;
- int rsync14 = 0;
- int rsync16 = 0;
- int ch;
- if (strcmp(progname, "gunzip") == 0 ||
- strcmp(progname, "zcat") == 0 ||
- strcmp(progname, "gzcat") == 0) {
- fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
- usage();
- }
- #define OPT_LIST "123456789acdfhF:GLNnMmqRrT:Vo:k:s:Z"
- while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
- switch (ch) {
- case 'G':
- gnu = 1;
- break;
- case 'Z':
- break;
- case '1': case '2': case '3':
- case '4': case '5': case '6':
- case '7': case '8': case '9':
- level = ch - '0';
- break;
- case 'c':
- /* Ignored for compatibility; zgz always uses -c */
- break;
- case 'f':
- fflag = 1;
- break;
- case 'N':
- nflag = 0;
- break;
- case 'n':
- nflag = 1;
- /* no break, n implies m */
- case 'm':
- mflag = 1;
- break;
- case 'M':
- mflag = 0;
- break;
- case 'q':
- qflag = 1;
- break;
- case 's':
- osflag = atoi(optarg);
- break;
- case 'F':
- case 'o':
- origname = optarg;
- break;
- case 'k':
- quirks = 1;
- if (strcmp(optarg, "buggy-bsd") == 0) {
- /* certain archives made with older versions of
- * BSD variants of gzip */
- /* no name or timestamp information */
- nflag = 1;
- mflag = 1;
- /* maximum compression but without indicating so */
- level = 9;
- xflag = 0;
- } else if (strcmp(optarg, "ntfs") == 0) {
- ntfs_quirk = 1;
- /* no name or timestamp information */
- nflag = 1;
- mflag = 1;
- /* osflag is NTFS */
- osflag = GZIP_OS_NTFS;
- } else if (strcmp(optarg, "perl") == 0) {
- /* Perl's Compress::Raw::Zlib */
- memlevel = 9;
- /* no name or timestamp information */
- nflag = 1;
- mflag = 1;
- /* maximum compression but without indicating so */
- level = 9;
- xflag = 0;
- } else {
- fprintf(stderr, "%s: unknown quirk!\n", progname);
- usage();
- }
- break;
- case 'T':
- timestamp = atoi(optarg);
- break;
- case 'R':
- rsync = 1;
- break;
- case 'r':
- rsync14 = 1;
- break;
- case 'D':
- rsync16 = 1;
- break;
- case 'd':
- fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
- usage();
- break;
- case 'a':
- fprintf(stderr, "%s: option --ascii ignored on this version\n", progname);
- break;
- case 'V':
- display_version();
- /* NOTREACHED */
- case 'L':
- display_license();
- /* NOT REACHED */
- default:
- usage();
- /* NOTREACHED */
- }
- }
- argv += optind;
- argc -= optind;
- if (argc != 0) {
- fprintf(stderr, "%s: filenames not supported; use stdin and stdout\n", progname);
- return 1;
- }
- if (fflag == 0 && isatty(STDOUT_FILENO))
- maybe_errx("standard output is a terminal -- ignoring");
- if (nflag)
- origname = NULL;
- if (mflag)
- timestamp = 0;
- if (gnu) {
- if (quirks) {
- fprintf(stderr, "%s: quirks not supported with --gnu\n", progname);
- return 1;
- }
- gnuzip(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, osflag, rsync, rsync14, rsync16);
- } else {
- if (rsync || rsync14 || rsync16) {
- fprintf(stderr, "%s: --rsyncable not supported with --zlib\n", progname);
- return 1;
- }
- gz_compress(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, memlevel, osflag, xflag, ntfs_quirk);
- }
- return 0;
- }
- /* maybe print an error */
- void
- maybe_err(const char *fmt, ...)
- {
- va_list ap;
- if (qflag == 0) {
- va_start(ap, fmt);
- vwarn(fmt, ap);
- va_end(ap);
- }
- exit(1);
- }
- /* ... without an errno. */
- void
- maybe_errx(const char *fmt, ...)
- {
- va_list ap;
- if (qflag == 0) {
- va_start(ap, fmt);
- vwarnx(fmt, ap);
- va_end(ap);
- }
- exit(1);
- }
- /* compress input to output. */
- static void
- gz_compress(int in, int out, const char *origname, uint32_t mtime, int level, int memlevel, int osflag, int xflag, int ntfs_quirk)
- {
- z_stream z;
- char *outbufp, *inbufp;
- off_t in_tot = 0;
- ssize_t in_size;
- int i, error;
- uLong crc;
- outbufp = malloc(BUFLEN);
- inbufp = malloc(BUFLEN);
- if (outbufp == NULL || inbufp == NULL)
- maybe_err("malloc failed");
- memset(&z, 0, sizeof z);
- z.zalloc = Z_NULL;
- z.zfree = Z_NULL;
- z.opaque = 0;
- i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
- GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
- origname ? ORIG_NAME : 0,
- mtime & 0xff,
- (mtime >> 8) & 0xff,
- (mtime >> 16) & 0xff,
- (mtime >> 24) & 0xff,
- xflag >= 0 ? xflag :
- level == 1 ? 4 : level == 9 ? 2 : 0,
- osflag, origname ? origname : "");
- if (i >= BUFLEN)
- /* this need PATH_MAX > BUFLEN ... */
- maybe_err("snprintf");
- if (origname)
- i++;
- z.next_out = (unsigned char *)outbufp + i;
- z.avail_out = BUFLEN - i;
- error = deflateInit2(&z, level, Z_DEFLATED,
- (-MAX_WBITS), memlevel, Z_DEFAULT_STRATEGY);
- if (error != Z_OK)
- maybe_err("deflateInit2 failed");
- crc = crc32(0L, Z_NULL, 0);
- for (;;) {
- if (z.avail_out == 0) {
- if (write(out, outbufp, BUFLEN) != BUFLEN)
- maybe_err("write");
- z.next_out = (unsigned char *)outbufp;
- z.avail_out = BUFLEN;
- }
- if (z.avail_in == 0) {
- in_size = read(in, inbufp, BUFLEN);
- if (in_size < 0)
- maybe_err("read");
- if (in_size == 0)
- break;
- crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
- in_tot += in_size;
- z.next_in = (unsigned char *)inbufp;
- z.avail_in = in_size;
- }
- error = deflate(&z, Z_NO_FLUSH);
- if (error != Z_OK && error != Z_STREAM_END)
- maybe_errx("deflate failed");
- }
- /* clean up */
- for (;;) {
- size_t len;
- ssize_t w;
- error = deflate(&z, Z_FINISH);
- if (error != Z_OK && error != Z_STREAM_END)
- maybe_errx("deflate failed");
- len = (char *)z.next_out - outbufp;
- /* for a really strange reason, that
- * particular byte is decremented */
- if (ntfs_quirk)
- outbufp[10]--;
- w = write(out, outbufp, len);
- if (w == -1 || (size_t)w != len)
- maybe_err("write");
- z.next_out = (unsigned char *)outbufp;
- z.avail_out = BUFLEN;
- if (error == Z_STREAM_END)
- break;
- }
- if (deflateEnd(&z) != Z_OK)
- maybe_errx("deflateEnd failed");
- if (ntfs_quirk) {
- /* write NTFS tail magic (?) */
- i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c",
- 0x00, 0x00, 0xff, 0xff, 0x03, 0x00);
- if (i != 6)
- maybe_err("snprintf");
- if (write(out, outbufp, i) != i)
- maybe_err("write");
- }
- /* write CRC32 and input size (ISIZE) at the tail */
- i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
- (int)crc & 0xff,
- (int)(crc >> 8) & 0xff,
- (int)(crc >> 16) & 0xff,
- (int)(crc >> 24) & 0xff,
- (int)in_tot & 0xff,
- (int)(in_tot >> 8) & 0xff,
- (int)(in_tot >> 16) & 0xff,
- (int)(in_tot >> 24) & 0xff);
- if (i != 8)
- maybe_err("snprintf");
- if (write(out, outbufp, i) != i)
- maybe_err("write");
- free(inbufp);
- free(outbufp);
- }
- /* display usage */
- static void
- usage(void)
- {
- fprintf(stderr, "%s\n", gzip_version);
- fprintf(stderr,
- "usage: zgz [-" OPT_LIST "] < <file> > <file>\n"
- " -G --gnu use GNU gzip implementation\n"
- " -Z --zlib use zlib's implementation (default)\n"
- " -1 --fast fastest (worst) compression\n"
- " -2 .. -8 set compression level\n"
- " -9 --best best (slowest) compression\n"
- " -f --force force writing compressed data to a terminal\n"
- " -N --name save or restore original file name and time stamp\n"
- " -n --no-name don't save original file name or time stamp\n"
- " -m --no-timestamp don't save original time stamp\n"
- " -M --force-timestemp save the timestamp even if -n was passed\n"
- " -q --quiet output no warnings\n"
- " -V --version display program version\n"
- " -h --help display this help\n"
- " -o NAME\n"
- " --original-name NAME use NAME as the original file name\n"
- " -F NAME --filename NAME same as --original-name\n"
- " -s --osflag set the OS flag to something different than 03 (Unix)\n"
- " -T --timestamp SECONDS set the timestamp to the specified number of seconds\n"
- " \ngnu-specific options:\n"
- " -R --rsyncable make rsync-friendly archive\n"
- " -r --new-rsyncable make rsync-friendly archive (new version)\n"
- " \nzlib-specific options:\n"
- " -k --quirk QUIRK enable a format quirk (buggy-bsd, ntfs, perl)\n");
- exit(0);
- }
- /* display the license information of NetBSD gzip */
- static void
- display_license(void)
- {
- fprintf(stderr, "%s\n", gzip_version);
- fprintf(stderr, "%s\n", gzip_copyright);
- exit(0);
- }
- /* display the version of NetBSD gzip */
- static void
- display_version(void)
- {
- fprintf(stderr, "%s\n", gzip_version);
- exit(0);
- }
|