2 * Authors: Faidon Liambotis <paravoid@debian.org>
3 * Josh Triplett <josh@joshtriplett.org>
5 * This is a zlib-based gzip that is heavily based on NetBSD's gzip,
6 * developed by Matthew R. Green.
8 * This is suited for gzip regeneration and is part of pristine-tar.
9 * As such, it adds some extra options which are needed to successfully
10 * reproduce the gzips out there and removes features of the original
11 * implementation that were not relevant (e.g. decompression)
13 * It also has a mode to generate old bzip2 files.
15 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
16 * Copyright (c) 2007 Faidon Liambotis
17 * Copyright (c) 2008 Josh Triplett
18 * Copyright (c) 2010 Joey Hess
19 * All rights reserved.
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
24 * 1. Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 * 3. The name of the author may not be used to endorse or promote products
30 * derived from this software without specific prior written permission.
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
33 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
35 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
36 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
37 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
39 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
40 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 * gzip.c -- GPL free gzip using zlib.
48 * RFC 1950 covers the zlib format
49 * RFC 1951 covers the deflate format
50 * RFC 1952 covers the gzip format
56 #include <sys/param.h>
75 extern void gnuzip(int in, int out, char *origname, unsigned long timestamp, int level, int osflag, int rsync, int newrsync);
76 extern void old_bzip2(int level);
78 #define BUFLEN (64 * 1024)
80 #define GZIP_MAGIC0 0x1F
81 #define GZIP_MAGIC1 0x8B
84 #define EXTRA_FIELD 0x04
85 #define ORIG_NAME 0x08
88 #define GZIP_OS_UNIX 3 /* Unix */
89 #define GZIP_OS_NTFS 11 /* NTFS */
91 static const char gzip_version[] = "zgz 20100613 based on NetBSD gzip 20060927, GNU gzip 1.3.12, and bzip2 0.9.5d";
93 static const char gzip_copyright[] = \
94 " Authors: Faidon Liambotis <paravoid@debian.org>\n"
95 " Josh Triplett <josh@joshtriplett.org>\n"
97 " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
98 " Copyright (c) 2007 Faidon Liambotis\n"
99 " Copyright (c) 2008 Josh Triplett\n"
100 " * All rights reserved.\n"
102 " * Redistribution and use in source and binary forms, with or without\n"
103 " * modification, are permitted provided that the following conditions\n"
105 " * 1. Redistributions of source code must retain the above copyright\n"
106 " * notice, this list of conditions and the following disclaimer.\n"
107 " * 2. Redistributions in binary form must reproduce the above copyright\n"
108 " * notice, this list of conditions and the following disclaimer in the\n"
109 " * documentation and/or other materials provided with the distribution.\n"
110 " * 3. The name of the author may not be used to endorse or promote products\n"
111 " * derived from this software without specific prior written permission.\n"
113 " * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
114 " * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
115 " * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
116 " * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
117 " * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
118 " * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
119 " * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
120 " * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
121 " * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
122 " * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
125 static int qflag; /* quiet mode */
127 static void maybe_err(const char *fmt, ...)
128 __attribute__((__format__(__printf__, 1, 2),noreturn));
129 static void maybe_errx(const char *fmt, ...)
130 __attribute__((__format__(__printf__, 1, 2),noreturn));
131 static void gz_compress(int, int, const char *, uint32_t, int, int, int, int, int);
132 static void usage(void);
133 static void display_version(void);
134 static void display_license(void);
135 static void shamble(char *, int, char *);
136 static void rebrain(char *, char *, int, char *);
138 int main(int, char **p);
140 static const struct option longopts[] = {
141 { "stdout", no_argument, 0, 'c' },
142 { "to-stdout", no_argument, 0, 'c' },
143 { "decompress", no_argument, 0, 'd' },
144 { "uncompress", no_argument, 0, 'd' },
145 { "force", no_argument, 0, 'f' },
146 { "help", no_argument, 0, 'h' },
147 { "no-name", no_argument, 0, 'n' },
148 { "name", no_argument, 0, 'N' },
149 { "quiet", no_argument, 0, 'q' },
150 { "fast", no_argument, 0, '1' },
151 { "best", no_argument, 0, '9' },
152 { "ascii", no_argument, 0, 'a' },
154 { "gnu", no_argument, 0, 'G' },
155 { "old-bzip2", no_argument, 0, 'O' },
156 { "suse-bzip2", no_argument, 0, 'S' },
157 { "suse-pbzip2", no_argument, 0, 'P' },
158 { "zlib", no_argument, 0, 'Z' },
159 { "rsyncable", no_argument, 0, 'R' },
160 { "new-rsyncable", no_argument, 0, 'r' },
161 { "no-timestamp", no_argument, 0, 'm' },
162 { "force-timestamp", no_argument, 0, 'M' },
163 { "timestamp", required_argument, 0, 'T' },
164 { "osflag", required_argument, 0, 's' },
165 { "original-name", required_argument, 0, 'o' },
166 { "filename", required_argument, 0, 'F' },
167 { "quirk", required_argument, 0, 'k' },
169 { "version", no_argument, 0, 'V' },
170 { "license", no_argument, 0, 'L' },
171 { NULL, no_argument, 0, 0 },
175 main(int argc, char **argv)
177 const char *progname = argv[0];
183 char *origname = NULL;
184 uint32_t timestamp = 0;
185 int memlevel = 8; /* zlib's default */
192 int osflag = GZIP_OS_UNIX;
197 if (strcmp(progname, "gunzip") == 0 ||
198 strcmp(progname, "zcat") == 0 ||
199 strcmp(progname, "gzcat") == 0) {
200 fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
204 #define OPT_LIST "123456789acdfhF:GLNnMmqRrT:Vo:k:s:ZOSP"
206 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
222 case '1': case '2': case '3':
223 case '4': case '5': case '6':
224 case '7': case '8': case '9':
228 /* Ignored for compatibility; zgz always uses -c */
238 /* no break, n implies m */
249 osflag = atoi(optarg);
257 if (strcmp(optarg, "buggy-bsd") == 0) {
258 /* certain archives made with older versions of
259 * BSD variants of gzip */
261 /* no name or timestamp information */
264 /* maximum compression but without indicating so */
267 } else if (strcmp(optarg, "ntfs") == 0) {
269 /* no name or timestamp information */
273 osflag = GZIP_OS_NTFS;
274 } else if (strcmp(optarg, "perl") == 0) {
275 /* Perl's Compress::Raw::Zlib */
278 /* no name or timestamp information */
281 /* maximum compression but without indicating so */
285 fprintf(stderr, "%s: unknown quirk!\n", progname);
290 timestamp = atoi(optarg);
299 fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
303 fprintf(stderr, "%s: option --ascii ignored on this version\n", progname);
320 fprintf(stderr, "%s: filenames not supported; use stdin and stdout\n", progname);
324 if (fflag == 0 && isatty(STDOUT_FILENO))
325 maybe_errx("standard output is a terminal -- ignoring");
334 fprintf(stderr, "%s: quirks not supported with --gnu\n", progname);
337 gnuzip(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, osflag, rsync, new_rsync);
340 fprintf(stderr, "%s: quirks not supported with --old-bzip2\n", progname);
345 shamble("suse-bzip2/bzip2", level, "");
346 } else if (pbzsuse) {
347 rebrain("suse-bzip2", "pbzip2", level, "");
349 if (rsync || new_rsync) {
350 fprintf(stderr, "%s: --rsyncable not supported with --zlib\n", progname);
354 gz_compress(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, memlevel, osflag, xflag, ntfs_quirk);
359 /* maybe print an error */
361 maybe_err(const char *fmt, ...)
373 /* ... without an errno. */
375 maybe_errx(const char *fmt, ...)
387 /* compress input to output. */
389 gz_compress(int in, int out, const char *origname, uint32_t mtime, int level, int memlevel, int osflag, int xflag, int ntfs_quirk)
392 char *outbufp, *inbufp;
398 outbufp = malloc(BUFLEN);
399 inbufp = malloc(BUFLEN);
400 if (outbufp == NULL || inbufp == NULL)
401 maybe_err("malloc failed");
403 memset(&z, 0, sizeof z);
408 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
409 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
410 origname ? ORIG_NAME : 0,
413 (mtime >> 16) & 0xff,
414 (mtime >> 24) & 0xff,
416 level == 1 ? 4 : level == 9 ? 2 : 0,
417 osflag, origname ? origname : "");
419 /* this need PATH_MAX > BUFLEN ... */
420 maybe_err("snprintf");
424 z.next_out = (unsigned char *)outbufp + i;
425 z.avail_out = BUFLEN - i;
427 error = deflateInit2(&z, level, Z_DEFLATED,
428 (-MAX_WBITS), memlevel, Z_DEFAULT_STRATEGY);
430 maybe_err("deflateInit2 failed");
432 crc = crc32(0L, Z_NULL, 0);
434 if (z.avail_out == 0) {
435 if (write(out, outbufp, BUFLEN) != BUFLEN)
438 z.next_out = (unsigned char *)outbufp;
439 z.avail_out = BUFLEN;
442 if (z.avail_in == 0) {
443 in_size = read(in, inbufp, BUFLEN);
449 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
451 z.next_in = (unsigned char *)inbufp;
452 z.avail_in = in_size;
455 error = deflate(&z, Z_NO_FLUSH);
456 if (error != Z_OK && error != Z_STREAM_END)
457 maybe_errx("deflate failed");
465 error = deflate(&z, Z_FINISH);
466 if (error != Z_OK && error != Z_STREAM_END)
467 maybe_errx("deflate failed");
469 len = (char *)z.next_out - outbufp;
471 /* for a really strange reason, that
472 * particular byte is decremented */
476 w = write(out, outbufp, len);
477 if (w == -1 || (size_t)w != len)
479 z.next_out = (unsigned char *)outbufp;
480 z.avail_out = BUFLEN;
482 if (error == Z_STREAM_END)
486 if (deflateEnd(&z) != Z_OK)
487 maybe_errx("deflateEnd failed");
490 /* write NTFS tail magic (?) */
491 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c",
492 0x00, 0x00, 0xff, 0xff, 0x03, 0x00);
494 maybe_err("snprintf");
495 if (write(out, outbufp, i) != i)
499 /* write CRC32 and input size (ISIZE) at the tail */
500 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
502 (int)(crc >> 8) & 0xff,
503 (int)(crc >> 16) & 0xff,
504 (int)(crc >> 24) & 0xff,
506 (int)(in_tot >> 8) & 0xff,
507 (int)(in_tot >> 16) & 0xff,
508 (int)(in_tot >> 24) & 0xff);
510 maybe_err("snprintf");
511 if (write(out, outbufp, i) != i)
518 /* runs an external, reanimated compressor program */
520 shamble(char *zombie, int level, char *opts)
523 sprintf(buf, "%s/%s -%i %s", ZGZ_LIB, zombie, level, opts);
527 /* swaps in a different library and runs a system program */
529 rebrain(char *zombie, char *program, int level, char *opts)
532 sprintf(buf, "LD_LIBRARY_PATH=%s/%s %s -%i %s",
533 ZGZ_LIB, zombie, program, level, opts);
542 fprintf(stderr, "%s\n", gzip_version);
544 "usage: zgz [-" OPT_LIST "] < <file> > <file>\n"
545 " -G --gnu use GNU gzip implementation\n"
546 " -Z --zlib use zlib's implementation (default)\n"
547 " -O --old-bzip2 generate bzip2 (0.9.5d) output\n"
548 " -S --suse-bzip2 generate suse bzip2 output\n"
549 " -P --suse-pbzip2 generate suse pbzip2 output\n"
550 " -1 --fast fastest (worst) compression\n"
551 " -2 .. -8 set compression level\n"
552 " -9 --best best (slowest) compression\n"
553 " -f --force force writing compressed data to a terminal\n"
554 " -N --name save or restore original file name and time stamp\n"
555 " -n --no-name don't save original file name or time stamp\n"
556 " -m --no-timestamp don't save original time stamp\n"
557 " -M --force-timestemp save the timestamp even if -n was passed\n"
558 " -q --quiet output no warnings\n"
559 " -V --version display program version\n"
560 " -h --help display this help\n"
562 " --original-name NAME use NAME as the original file name\n"
563 " -F NAME --filename NAME same as --original-name\n"
564 " -s --osflag set the OS flag to something different than 03 (Unix)\n"
565 " -T --timestamp SECONDS set the timestamp to the specified number of seconds\n"
566 " \ngnu-specific options:\n"
567 " -R --rsyncable make rsync-friendly archive\n"
568 " -r --new-rsyncable make rsync-friendly archive (new version)\n"
569 " \nzlib-specific options:\n"
570 " -k --quirk QUIRK enable a format quirk (buggy-bsd, ntfs, perl)\n");
574 /* display the license information of NetBSD gzip */
576 display_license(void)
579 fprintf(stderr, "%s\n", gzip_version);
580 fprintf(stderr, "%s\n", gzip_copyright);
584 /* display the version of NetBSD gzip */
586 display_version(void)
589 fprintf(stderr, "%s\n", gzip_version);