c7de4626cf6d4b09bdfc11090e9f8ce43961b3c1
[tools/pristine-tar.git] / zgz / zgz.c
1 /*
2  * Authors: Faidon Liambotis <paravoid@debian.org>
3  *          Josh Triplett <josh@joshtriplett.org>
4  *
5  * This is a zlib-based gzip that is heavily based on NetBSD's gzip,
6  * developed by Matthew R. Green.
7  *
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)
12  *
13  * It also has a mode to generate old bzip2 files.
14  *
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.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
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.
31  *
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
42  * SUCH DAMAGE.
43  */
44
45 /*
46  * gzip.c -- GPL free gzip using zlib.
47  *
48  * RFC 1950 covers the zlib format
49  * RFC 1951 covers the deflate format
50  * RFC 1952 covers the gzip format
51  *
52  */
53
54 #define _GNU_SOURCE
55
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <sys/time.h>
59
60 #include <inttypes.h>
61 #include <unistd.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <err.h>
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <zlib.h>
69 #include <fts.h>
70 #include <libgen.h>
71 #include <stdarg.h>
72 #include <getopt.h>
73 #include <time.h>
74
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);
77
78 #define BUFLEN          (64 * 1024)
79
80 #define GZIP_MAGIC0     0x1F
81 #define GZIP_MAGIC1     0x8B
82
83 #define HEAD_CRC        0x02
84 #define EXTRA_FIELD     0x04
85 #define ORIG_NAME       0x08
86 #define COMMENT         0x10
87
88 #define GZIP_OS_UNIX    3       /* Unix */
89 #define GZIP_OS_NTFS    11      /* NTFS */
90
91 static  const char      gzip_version[] = "zgz 20100613 based on NetBSD gzip 20060927, GNU gzip 1.3.12, and bzip2 0.9.5d";
92
93 static  const char      gzip_copyright[] = \
94 " Authors: Faidon Liambotis <paravoid@debian.org>\n"
95 "          Josh Triplett <josh@joshtriplett.org>\n"
96 "\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"
101 " *\n"
102 " * Redistribution and use in source and binary forms, with or without\n"
103 " * modification, are permitted provided that the following conditions\n"
104 " * are met:\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"
112 " *\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"
123 " * SUCH DAMAGE.";
124
125 static  int     qflag;                  /* quiet mode */
126
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 *);
137
138 int main(int, char **p);
139
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' },
153         /* new options */
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' },
168         /* end */
169         { "version",            no_argument,            0,      'V' },
170         { "license",            no_argument,            0,      'L' },
171         { NULL,                 no_argument,            0,       0  },
172 };
173
174 int
175 main(int argc, char **argv)
176 {
177         const char *progname = argv[0];
178         int gnu = 0;
179         int bzold = 0;
180         int bzsuse = 0;
181         int pbzsuse = 0;
182         int quirks = 0;
183         char *origname = NULL;
184         uint32_t timestamp = 0;
185         int memlevel = 8; /* zlib's default */
186         int nflag = 0;
187         int mflag = 0;
188         int fflag = 0;
189         int xflag = -1;
190         int ntfs_quirk = 0;
191         int level = 6;
192         int osflag = GZIP_OS_UNIX;
193         int rsync = 0;
194         int new_rsync = 0;
195         int ch;
196
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);
201                 usage();
202         }
203
204 #define OPT_LIST "123456789acdfhF:GLNnMmqRrT:Vo:k:s:ZOSP"
205
206         while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
207                 switch (ch) {
208                 case 'G':
209                         gnu = 1;
210                         break;
211                 case 'O':
212                         bzold = 1;
213                         break;
214                 case 'S':
215                         bzsuse = 1;
216                         break;
217                 case 'P':
218                         pbzsuse = 1;
219                         break;
220                 case 'Z':
221                         break;
222                 case '1': case '2': case '3':
223                 case '4': case '5': case '6':
224                 case '7': case '8': case '9':
225                         level = ch - '0';
226                         break;
227                 case 'c':
228                         /* Ignored for compatibility; zgz always uses -c */
229                         break;
230                 case 'f':
231                         fflag = 1;
232                         break;
233                 case 'N':
234                         nflag = 0;
235                         break;
236                 case 'n':
237                         nflag = 1;
238                         /* no break, n implies m */
239                 case 'm':
240                         mflag = 1;
241                         break;
242                 case 'M':
243                         mflag = 0;
244                         break;
245                 case 'q':
246                         qflag = 1;
247                         break;
248                 case 's':
249                         osflag = atoi(optarg);
250                         break;
251                 case 'F':
252                 case 'o':
253                         origname = optarg;
254                         break;
255                 case 'k':
256                         quirks = 1;
257                         if (strcmp(optarg, "buggy-bsd") == 0) {
258                                 /* certain archives made with older versions of
259                                  * BSD variants of gzip */
260
261                                 /* no name or timestamp information */
262                                 nflag = 1;
263                                 mflag = 1;
264                                 /* maximum compression but without indicating so */
265                                 level = 9;
266                                 xflag = 0;
267                         } else if (strcmp(optarg, "ntfs") == 0) {
268                                 ntfs_quirk = 1;
269                                 /* no name or timestamp information */
270                                 nflag = 1;
271                                 mflag = 1;
272                                 /* osflag is NTFS */
273                                 osflag = GZIP_OS_NTFS;
274                         } else if (strcmp(optarg, "perl") == 0) {
275                                 /* Perl's Compress::Raw::Zlib */
276                                 memlevel = 9;
277
278                                 /* no name or timestamp information */
279                                 nflag = 1;
280                                 mflag = 1;
281                                 /* maximum compression but without indicating so */
282                                 level = 9;
283                                 xflag = 0;
284                         } else {
285                                 fprintf(stderr, "%s: unknown quirk!\n", progname);
286                                 usage();
287                         }
288                         break;
289                 case 'T':
290                         timestamp = atoi(optarg);
291                         break;
292                 case 'R':
293                         rsync = 1;
294                         break;
295                 case 'r':
296                         new_rsync = 1;
297                         break;
298                 case 'd':
299                         fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
300                         usage();
301                         break;
302                 case 'a':
303                         fprintf(stderr, "%s: option --ascii ignored on this version\n", progname);
304                         break;
305                 case 'V':
306                         display_version();
307                         /* NOTREACHED */
308                 case 'L':
309                         display_license();
310                         /* NOT REACHED */
311                 default:
312                         usage();
313                         /* NOTREACHED */
314                 }
315         }
316         argv += optind;
317         argc -= optind;
318
319         if (argc != 0) {
320                 fprintf(stderr, "%s: filenames not supported; use stdin and stdout\n", progname);
321                 return 1;
322         }
323
324         if (fflag == 0 && isatty(STDOUT_FILENO))
325                 maybe_errx("standard output is a terminal -- ignoring");
326
327         if (nflag)
328                 origname = NULL;
329         if (mflag)
330                 timestamp = 0;
331
332         if (gnu) {
333                 if (quirks) {
334                         fprintf(stderr, "%s: quirks not supported with --gnu\n", progname);
335                         return 1;
336                 }
337                 gnuzip(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, osflag, rsync, new_rsync);
338         } else if (bzold) {
339                 if (quirks) {
340                         fprintf(stderr, "%s: quirks not supported with --old-bzip2\n", progname);
341                         return 1;
342                 }
343                 old_bzip2(level);
344         } else if (bzsuse) {
345                 shamble("suse-bzip2/bzip2", level, "");
346         } else if (pbzsuse) {
347                 rebrain("suse-bzip2", "pbzip2", level, "");
348         } else {
349                 if (rsync || new_rsync) {
350                         fprintf(stderr, "%s: --rsyncable not supported with --zlib\n", progname);
351                         return 1;
352                 }
353
354                 gz_compress(STDIN_FILENO, STDOUT_FILENO, origname, timestamp, level, memlevel, osflag, xflag, ntfs_quirk);
355         }
356         return 0;
357 }
358
359 /* maybe print an error */
360 void
361 maybe_err(const char *fmt, ...)
362 {
363         va_list ap;
364
365         if (qflag == 0) {
366                 va_start(ap, fmt);
367                 vwarn(fmt, ap);
368                 va_end(ap);
369         }
370         exit(1);
371 }
372
373 /* ... without an errno. */
374 void
375 maybe_errx(const char *fmt, ...)
376 {
377         va_list ap;
378
379         if (qflag == 0) {
380                 va_start(ap, fmt);
381                 vwarnx(fmt, ap);
382                 va_end(ap);
383         }
384         exit(1);
385 }
386
387 /* compress input to output. */
388 static void
389 gz_compress(int in, int out, const char *origname, uint32_t mtime, int level, int memlevel, int osflag, int xflag, int ntfs_quirk)
390 {
391         z_stream z;
392         char *outbufp, *inbufp;
393         off_t in_tot = 0;
394         ssize_t in_size;
395         int i, error;
396         uLong crc;
397
398         outbufp = malloc(BUFLEN);
399         inbufp = malloc(BUFLEN);
400         if (outbufp == NULL || inbufp == NULL)
401                 maybe_err("malloc failed");
402
403         memset(&z, 0, sizeof z);
404         z.zalloc = Z_NULL;
405         z.zfree = Z_NULL;
406         z.opaque = 0;
407
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,
411                      mtime & 0xff,
412                      (mtime >> 8) & 0xff,
413                      (mtime >> 16) & 0xff,
414                      (mtime >> 24) & 0xff,
415                      xflag >= 0 ? xflag :
416                      level == 1 ? 4 : level == 9 ? 2 : 0,
417                      osflag, origname ? origname : "");
418         if (i >= BUFLEN)     
419                 /* this need PATH_MAX > BUFLEN ... */
420                 maybe_err("snprintf");
421         if (origname)
422                 i++;
423
424         z.next_out = (unsigned char *)outbufp + i;
425         z.avail_out = BUFLEN - i;
426
427         error = deflateInit2(&z, level, Z_DEFLATED,
428                              (-MAX_WBITS), memlevel, Z_DEFAULT_STRATEGY);
429         if (error != Z_OK)
430                 maybe_err("deflateInit2 failed");
431
432         crc = crc32(0L, Z_NULL, 0);
433         for (;;) {
434                 if (z.avail_out == 0) {
435                         if (write(out, outbufp, BUFLEN) != BUFLEN)
436                                 maybe_err("write");
437
438                         z.next_out = (unsigned char *)outbufp;
439                         z.avail_out = BUFLEN;
440                 }
441
442                 if (z.avail_in == 0) {
443                         in_size = read(in, inbufp, BUFLEN);
444                         if (in_size < 0)
445                                 maybe_err("read");
446                         if (in_size == 0)
447                                 break;
448
449                         crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
450                         in_tot += in_size;
451                         z.next_in = (unsigned char *)inbufp;
452                         z.avail_in = in_size;
453                 }
454
455                 error = deflate(&z, Z_NO_FLUSH);
456                 if (error != Z_OK && error != Z_STREAM_END)
457                         maybe_errx("deflate failed");
458         }
459
460         /* clean up */
461         for (;;) {
462                 size_t len;
463                 ssize_t w;
464
465                 error = deflate(&z, Z_FINISH);
466                 if (error != Z_OK && error != Z_STREAM_END)
467                         maybe_errx("deflate failed");
468
469                 len = (char *)z.next_out - outbufp;
470
471                 /* for a really strange reason, that 
472                  * particular byte is decremented */
473                 if (ntfs_quirk)
474                         outbufp[10]--;
475
476                 w = write(out, outbufp, len);
477                 if (w == -1 || (size_t)w != len)
478                         maybe_err("write");
479                 z.next_out = (unsigned char *)outbufp;
480                 z.avail_out = BUFLEN;
481
482                 if (error == Z_STREAM_END)
483                         break;
484         }
485
486         if (deflateEnd(&z) != Z_OK)
487                 maybe_errx("deflateEnd failed");
488
489         if (ntfs_quirk) {
490                 /* write NTFS tail magic (?) */
491                 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c", 
492                         0x00, 0x00, 0xff, 0xff, 0x03, 0x00);
493                 if (i != 6)
494                         maybe_err("snprintf");
495                 if (write(out, outbufp, i) != i)
496                         maybe_err("write");
497         }
498
499         /* write CRC32 and input size (ISIZE) at the tail */
500         i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 
501                  (int)crc & 0xff,
502                  (int)(crc >> 8) & 0xff,
503                  (int)(crc >> 16) & 0xff,
504                  (int)(crc >> 24) & 0xff,
505                  (int)in_tot & 0xff,
506                  (int)(in_tot >> 8) & 0xff,
507                  (int)(in_tot >> 16) & 0xff,
508                  (int)(in_tot >> 24) & 0xff);
509         if (i != 8)
510                 maybe_err("snprintf");
511         if (write(out, outbufp, i) != i)
512                 maybe_err("write");
513
514         free(inbufp);
515         free(outbufp);
516 }
517
518 /* runs an external, reanimated compressor program */
519 static  void
520 shamble(char *zombie, int level, char *opts)
521 {
522         char buf[128];
523         sprintf(buf, "%s/%s -%i %s", ZGZ_LIB, zombie, level, opts);
524         exit(system(buf));
525 }
526
527 /* swaps in a different library and runs a system program */
528 static  void
529 rebrain(char *zombie, char *program, int level, char *opts)
530 {
531         char buf[128];
532         sprintf(buf, "LD_LIBRARY_PATH=%s/%s %s -%i %s",
533                         ZGZ_LIB, zombie, program, level, opts);
534         exit(system(buf));
535 }
536
537 /* display usage */
538 static void
539 usage(void)
540 {
541
542         fprintf(stderr, "%s\n", gzip_version);
543         fprintf(stderr,
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"
561     " -o NAME\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");
571         exit(0);
572 }
573
574 /* display the license information of NetBSD gzip */
575 static void
576 display_license(void)
577 {
578
579         fprintf(stderr, "%s\n", gzip_version);
580         fprintf(stderr, "%s\n", gzip_copyright);
581         exit(0);
582 }
583
584 /* display the version of NetBSD gzip */
585 static void
586 display_version(void)
587 {
588
589         fprintf(stderr, "%s\n", gzip_version);
590         exit(0);
591 }