]> git.saurik.com Git - apple/file_cmds.git/blame - gzip/gzip.c
file_cmds-264.50.1.tar.gz
[apple/file_cmds.git] / gzip / gzip.c
CommitLineData
00337e45
A
1/* $NetBSD: gzip.c,v 1.105 2011/08/30 23:06:00 joerg Exp $ */
2
3/*-
4 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31#ifndef lint
32__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\
33 Matthew R. Green. All rights reserved.");
34__FBSDID("$FreeBSD: src/usr.bin/gzip/gzip.c,v 1.25 2011/10/10 06:37:32 delphij Exp $");
35#endif /* not lint */
36
37/*
38 * gzip.c -- GPL free gzip using zlib.
39 *
40 * RFC 1950 covers the zlib format
41 * RFC 1951 covers the deflate format
42 * RFC 1952 covers the gzip format
43 *
44 * TODO:
45 * - use mmap where possible
46 * - make bzip2/compress -v/-t/-l support work as well as possible
47 */
48
49#include <sys/param.h>
50#include <sys/stat.h>
51#include <sys/time.h>
52
53#include <inttypes.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <string.h>
57#include <stdlib.h>
58#include <err.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <zlib.h>
62#include <fts.h>
63#include <libgen.h>
64#include <stdarg.h>
65#include <getopt.h>
66#include <time.h>
67
68#ifdef __APPLE__
69#include <sys/attr.h>
70#include <copyfile.h>
71#include <get_compat.h>
72#endif /* __APPLE__ */
73
74/* what type of file are we dealing with */
75enum filetype {
76 FT_GZIP,
77#ifndef NO_BZIP2_SUPPORT
78 FT_BZIP2,
79#endif
80#ifndef NO_COMPRESS_SUPPORT
81 FT_Z,
82#endif
83#ifndef NO_PACK_SUPPORT
84 FT_PACK,
85#endif
86#ifndef NO_XZ_SUPPORT
87 FT_XZ,
88#endif
89 FT_LAST,
90 FT_UNKNOWN
91};
92
93#ifndef NO_BZIP2_SUPPORT
94#include <bzlib.h>
95
96#define BZ2_SUFFIX ".bz2"
97#define BZIP2_MAGIC "\102\132\150"
98#endif
99
100#ifndef NO_COMPRESS_SUPPORT
101#define Z_SUFFIX ".Z"
102#define Z_MAGIC "\037\235"
103#endif
104
105#ifndef NO_PACK_SUPPORT
106#define PACK_MAGIC "\037\036"
107#endif
108
109#ifndef NO_XZ_SUPPORT
110#include <lzma.h>
111#define XZ_SUFFIX ".xz"
112#define XZ_MAGIC "\3757zXZ"
113#endif
114
115#define GZ_SUFFIX ".gz"
116
117#define BUFLEN (64 * 1024)
118
119#define GZIP_MAGIC0 0x1F
120#define GZIP_MAGIC1 0x8B
121#define GZIP_OMAGIC1 0x9E
122
123#define GZIP_TIMESTAMP (off_t)4
124#define GZIP_ORIGNAME (off_t)10
125
126#define HEAD_CRC 0x02
127#define EXTRA_FIELD 0x04
128#define ORIG_NAME 0x08
129#define COMMENT 0x10
130
131#define OS_CODE 3 /* Unix */
132
133typedef struct {
134 const char *zipped;
135 int ziplen;
136 const char *normal; /* for unzip - must not be longer than zipped */
137} suffixes_t;
138static suffixes_t suffixes[] = {
139#define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
140 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
141#ifndef SMALL
142 SUFFIX(GZ_SUFFIX, ""),
143 SUFFIX(".z", ""),
144 SUFFIX("-gz", ""),
145 SUFFIX("-z", ""),
146 SUFFIX("_z", ""),
147 SUFFIX(".taz", ".tar"),
148 SUFFIX(".tgz", ".tar"),
149#ifndef NO_BZIP2_SUPPORT
150 SUFFIX(BZ2_SUFFIX, ""),
151 SUFFIX(".tbz", ".tar"),
152 SUFFIX(".tbz2", ".tar"),
153#endif
154#ifndef NO_COMPRESS_SUPPORT
155 SUFFIX(Z_SUFFIX, ""),
156#endif
157#ifndef NO_XZ_SUPPORT
158 SUFFIX(XZ_SUFFIX, ""),
159#endif
160 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
161#endif /* SMALL */
162#undef SUFFIX
163};
164#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
165#define SUFFIX_MAXLEN 30
166
167#ifdef __APPLE__
168static const char gzip_version[] = "Apple gzip " GZIP_APPLE_VERSION;
169#else
170static const char gzip_version[] = "FreeBSD gzip 20111009";
171#endif
172
173#ifndef SMALL
174static const char gzip_copyright[] = \
175" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
176" All rights reserved.\n"
177"\n"
178" Redistribution and use in source and binary forms, with or without\n"
179" modification, are permitted provided that the following conditions\n"
180" are met:\n"
181" 1. Redistributions of source code must retain the above copyright\n"
182" notice, this list of conditions and the following disclaimer.\n"
183" 2. Redistributions in binary form must reproduce the above copyright\n"
184" notice, this list of conditions and the following disclaimer in the\n"
185" documentation and/or other materials provided with the distribution.\n"
186"\n"
187" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
188" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
189" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
190" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
191" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
192" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
193" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
194" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
195" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
196" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
197" SUCH DAMAGE.";
198#endif
199
200static int cflag; /* stdout mode */
201static int dflag; /* decompress mode */
202static int lflag; /* list mode */
203static int numflag = 6; /* gzip -1..-9 value */
204
205#ifndef SMALL
206static int fflag; /* force mode */
207static int kflag; /* don't delete input files */
208static int nflag; /* don't save name/timestamp */
209static int Nflag; /* don't restore name/timestamp */
210static int qflag; /* quiet mode */
211static int rflag; /* recursive mode */
212static int tflag; /* test */
213static int vflag; /* verbose mode */
214static const char *remove_file = NULL; /* file to be removed upon SIGINT */
215#else
216#define qflag 0
217#define tflag 0
218#endif
219
220static int exit_value = 0; /* exit value */
221
222static char *infile; /* name of file coming in */
223
224#ifdef __APPLE__
225static bool zcat;
226#endif
227
228static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
229#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
230 !defined(NO_XZ_SUPPORT)
231static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
232#endif
233static void maybe_warn(const char *fmt, ...) __printflike(1, 2);
234static void maybe_warnx(const char *fmt, ...) __printflike(1, 2);
235static enum filetype file_gettype(u_char *);
236#ifdef SMALL
237#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
238#endif
239static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
240static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
241static off_t file_compress(char *, char *, size_t);
242static off_t file_uncompress(char *, char *, size_t);
243static void handle_pathname(char *);
244static void handle_file(char *, struct stat *);
245static void handle_stdin(void);
246static void handle_stdout(void);
247static void print_ratio(off_t, off_t, FILE *);
248static void print_list(int fd, off_t, const char *, time_t);
249static void usage(void) __dead2;
250static void display_version(void) __dead2;
251#ifndef SMALL
252static void display_license(void);
253static void sigint_handler(int);
254#endif
255static const suffixes_t *check_suffix(char *, int);
256static ssize_t read_retry(int, void *, size_t);
257
258#ifdef SMALL
259#define unlink_input(f, sb) unlink(f)
260#else
261static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
262static void prepend_gzip(char *, int *, char ***);
263static void handle_dir(char *);
264static void print_verbage(const char *, const char *, off_t, off_t);
265static void print_test(const char *, int);
266static void copymodes(int fd, const struct stat *, const char *file);
267static int check_outfile(const char *outfile);
268#endif
269
270#ifndef NO_BZIP2_SUPPORT
271static off_t unbzip2(int, int, char *, size_t, off_t *);
272#endif
273
274#ifndef NO_COMPRESS_SUPPORT
275static FILE *zdopen(int);
276static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
277#endif
278
279#ifndef NO_PACK_SUPPORT
280static off_t unpack(int, int, char *, size_t, off_t *);
281#endif
282
283#ifndef NO_XZ_SUPPORT
284static off_t unxz(int, int, char *, size_t, off_t *);
285#endif
286
287#ifdef SMALL
288#define getopt_long(a,b,c,d,e) getopt(a,b,c)
289#else
290static const struct option longopts[] = {
291 { "stdout", no_argument, 0, 'c' },
292 { "to-stdout", no_argument, 0, 'c' },
293 { "decompress", no_argument, 0, 'd' },
294 { "uncompress", no_argument, 0, 'd' },
295 { "force", no_argument, 0, 'f' },
296 { "help", no_argument, 0, 'h' },
297 { "keep", no_argument, 0, 'k' },
298 { "list", no_argument, 0, 'l' },
299 { "no-name", no_argument, 0, 'n' },
300 { "name", no_argument, 0, 'N' },
301 { "quiet", no_argument, 0, 'q' },
302 { "recursive", no_argument, 0, 'r' },
303 { "suffix", required_argument, 0, 'S' },
304 { "test", no_argument, 0, 't' },
305 { "verbose", no_argument, 0, 'v' },
306 { "version", no_argument, 0, 'V' },
307 { "fast", no_argument, 0, '1' },
308 { "best", no_argument, 0, '9' },
309 { "ascii", no_argument, 0, 'a' },
310 { "license", no_argument, 0, 'L' },
311 { NULL, no_argument, 0, 0 },
312};
313#endif
314
315int
316main(int argc, char **argv)
317{
318 const char *progname = getprogname();
319#ifndef SMALL
320 char *gzip;
321 int len;
322#endif
323 int ch;
324
325#ifndef SMALL
326 if ((gzip = getenv("GZIP")) != NULL)
327 prepend_gzip(gzip, &argc, &argv);
328 signal(SIGINT, sigint_handler);
329#endif
330
331 /*
332 * XXX
333 * handle being called `gunzip', `zcat' and `gzcat'
334 */
335 if (strcmp(progname, "gunzip") == 0)
336 dflag = 1;
337 else if (strcmp(progname, "zcat") == 0 ||
338 strcmp(progname, "gzcat") == 0)
339 dflag = cflag = 1;
340
341#ifdef __APPLE__
342 if (strcmp(progname, "zcat") == 0) {
343 zcat = true;
344 }
345#endif
346
347#ifdef SMALL
348#define OPT_LIST "123456789cdhlV"
349#else
350#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
351#endif
352
353 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
354 switch (ch) {
355 case '1': case '2': case '3':
356 case '4': case '5': case '6':
357 case '7': case '8': case '9':
358 numflag = ch - '0';
359 break;
360 case 'c':
361 cflag = 1;
362 break;
363 case 'd':
364 dflag = 1;
365 break;
366 case 'l':
367 lflag = 1;
368 dflag = 1;
369 break;
370 case 'V':
371 display_version();
372 /* NOTREACHED */
373#ifndef SMALL
374 case 'a':
375 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
376 break;
377 case 'f':
378 fflag = 1;
379 break;
380 case 'k':
381 kflag = 1;
382 break;
383 case 'L':
384 display_license();
385 /* NOT REACHED */
386 case 'N':
387 nflag = 0;
388 Nflag = 1;
389 break;
390 case 'n':
391 nflag = 1;
392 Nflag = 0;
393 break;
394 case 'q':
395 qflag = 1;
396 break;
397 case 'r':
398 rflag = 1;
399 break;
400 case 'S':
401 len = strlen(optarg);
402 if (len != 0) {
403 if (len > SUFFIX_MAXLEN)
404 errx(1, "incorrect suffix: '%s': too long", optarg);
405 suffixes[0].zipped = optarg;
406 suffixes[0].ziplen = len;
407 } else {
408 suffixes[NUM_SUFFIXES - 1].zipped = "";
409 suffixes[NUM_SUFFIXES - 1].ziplen = 0;
410 }
411 break;
412 case 't':
413 cflag = 1;
414 tflag = 1;
415 dflag = 1;
416 break;
417 case 'v':
418 vflag = 1;
419 break;
420#endif
421 default:
422 usage();
423 /* NOTREACHED */
424 }
425 }
426 argv += optind;
427 argc -= optind;
428
429 if (argc == 0) {
430 if (dflag) /* stdin mode */
431 handle_stdin();
432 else /* stdout mode */
433 handle_stdout();
434 } else {
435 do {
436 handle_pathname(argv[0]);
437 } while (*++argv);
438 }
439#ifndef SMALL
440 if (qflag == 0 && lflag && argc > 1)
441 print_list(-1, 0, "(totals)", 0);
442#endif
443 exit(exit_value);
444}
445
446/* maybe print a warning */
447void
448maybe_warn(const char *fmt, ...)
449{
450 va_list ap;
451
452 if (qflag == 0) {
453 va_start(ap, fmt);
454 vwarn(fmt, ap);
455 va_end(ap);
456 }
457 if (exit_value == 0)
458 exit_value = 1;
459}
460
461/* ... without an errno. */
462void
463maybe_warnx(const char *fmt, ...)
464{
465 va_list ap;
466
467 if (qflag == 0) {
468 va_start(ap, fmt);
469 vwarnx(fmt, ap);
470 va_end(ap);
471 }
472 if (exit_value == 0)
473 exit_value = 1;
474}
475
476/* maybe print an error */
477void
478maybe_err(const char *fmt, ...)
479{
480 va_list ap;
481
482 if (qflag == 0) {
483 va_start(ap, fmt);
484 vwarn(fmt, ap);
485 va_end(ap);
486 }
487 exit(2);
488}
489
490#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
491 !defined(NO_XZ_SUPPORT)
492/* ... without an errno. */
493void
494maybe_errx(const char *fmt, ...)
495{
496 va_list ap;
497
498 if (qflag == 0) {
499 va_start(ap, fmt);
500 vwarnx(fmt, ap);
501 va_end(ap);
502 }
503 exit(2);
504}
505#endif
506
507#ifndef SMALL
508/* split up $GZIP and prepend it to the argument list */
509static void
510prepend_gzip(char *gzip, int *argc, char ***argv)
511{
512 char *s, **nargv, **ac;
513 int nenvarg = 0, i;
514
515 /* scan how many arguments there are */
516 for (s = gzip;;) {
517 while (*s == ' ' || *s == '\t')
518 s++;
519 if (*s == 0)
520 goto count_done;
521 nenvarg++;
522 while (*s != ' ' && *s != '\t')
523 if (*s++ == 0)
524 goto count_done;
525 }
526count_done:
527 /* punt early */
528 if (nenvarg == 0)
529 return;
530
531 *argc += nenvarg;
532 ac = *argv;
533
534 nargv = (char **)malloc((*argc + 1) * sizeof(char *));
535 if (nargv == NULL)
536 maybe_err("malloc");
537
538 /* stash this away */
539 *argv = nargv;
540
541 /* copy the program name first */
542 i = 0;
543 nargv[i++] = *(ac++);
544
545 s = gzip;
546 for (;;) {
547 /* Skip whitespaces. */
548 while (*s == ' ' || *s == '\t')
549 s++;
550 if (*s == 0) {
551 goto copy_done;
552 }
553 nargv[i++] = s;
554 /* Find the end of this argument. */
555 while (*s != ' ' && *s != '\t')
556 if (*s++ == 0)
557 /* Argument followed by NUL. */
558 goto copy_done;
559 /* copy any unterminated args */
560 nargv[i-1] = strndup(nargv[i-1], s-nargv[i-1]);
561 if (nargv[i-1] == NULL)
562 maybe_err("strndup");
563 s++;
564 }
565copy_done:
566
567 /* copy the original arguments and a NULL */
568 while (*ac)
569 nargv[i++] = *(ac++);
570 nargv[i] = NULL;
571}
572#endif
573
574/* compress input to output. Return bytes read, -1 on error */
575static off_t
576gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
577{
578 z_stream z;
579 char *outbufp, *inbufp;
580 off_t in_tot = 0, out_tot = 0;
581 ssize_t in_size;
582 int i, error;
583 uLong crc;
584#ifdef SMALL
585 static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
586 0, 0, 0, 0,
587 0, OS_CODE };
588#endif
589
590 outbufp = malloc(BUFLEN);
591 inbufp = malloc(BUFLEN);
592 if (outbufp == NULL || inbufp == NULL) {
593 maybe_err("malloc failed");
594 goto out;
595 }
596
597 memset(&z, 0, sizeof z);
598 z.zalloc = Z_NULL;
599 z.zfree = Z_NULL;
600 z.opaque = 0;
601
602#ifdef SMALL
603 memcpy(outbufp, header, sizeof header);
604 i = sizeof header;
605#else
606 if (nflag != 0) {
607 mtime = 0;
608 origname = "";
609 }
610
611 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
612 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
613 *origname ? ORIG_NAME : 0,
614 mtime & 0xff,
615 (mtime >> 8) & 0xff,
616 (mtime >> 16) & 0xff,
617 (mtime >> 24) & 0xff,
618 numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
619 OS_CODE, origname);
620 if (i >= BUFLEN)
621 /* this need PATH_MAX > BUFLEN ... */
622 maybe_err("snprintf");
623 if (*origname)
624 i++;
625#endif
626
627 z.next_out = (unsigned char *)outbufp + i;
628 z.avail_out = BUFLEN - i;
629
630 error = deflateInit2(&z, numflag, Z_DEFLATED,
631 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
632 if (error != Z_OK) {
633 maybe_warnx("deflateInit2 failed");
634 in_tot = -1;
635 goto out;
636 }
637
638 crc = crc32(0L, Z_NULL, 0);
639 for (;;) {
640 if (z.avail_out == 0) {
641 if (write(out, outbufp, BUFLEN) != BUFLEN) {
642 maybe_warn("write");
643 out_tot = -1;
644 goto out;
645 }
646
647 out_tot += BUFLEN;
648 z.next_out = (unsigned char *)outbufp;
649 z.avail_out = BUFLEN;
650 }
651
652 if (z.avail_in == 0) {
653 in_size = read(in, inbufp, BUFLEN);
654 if (in_size < 0) {
655 maybe_warn("read");
656 in_tot = -1;
657 goto out;
658 }
659 if (in_size == 0)
660 break;
661
662 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
663 in_tot += in_size;
664 z.next_in = (unsigned char *)inbufp;
665 z.avail_in = in_size;
666 }
667
668 error = deflate(&z, Z_NO_FLUSH);
669 if (error != Z_OK && error != Z_STREAM_END) {
670 maybe_warnx("deflate failed");
671 in_tot = -1;
672 goto out;
673 }
674 }
675
676 /* clean up */
677 for (;;) {
678 size_t len;
679 ssize_t w;
680
681 error = deflate(&z, Z_FINISH);
682 if (error != Z_OK && error != Z_STREAM_END) {
683 maybe_warnx("deflate failed");
684 in_tot = -1;
685 goto out;
686 }
687
688 len = (char *)z.next_out - outbufp;
689
690 w = write(out, outbufp, len);
691 if (w == -1 || (size_t)w != len) {
692 maybe_warn("write");
693 out_tot = -1;
694 goto out;
695 }
696 out_tot += len;
697 z.next_out = (unsigned char *)outbufp;
698 z.avail_out = BUFLEN;
699
700 if (error == Z_STREAM_END)
701 break;
702 }
703
704 if (deflateEnd(&z) != Z_OK) {
705 maybe_warnx("deflateEnd failed");
706 in_tot = -1;
707 goto out;
708 }
709
710 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
711 (int)crc & 0xff,
712 (int)(crc >> 8) & 0xff,
713 (int)(crc >> 16) & 0xff,
714 (int)(crc >> 24) & 0xff,
715 (int)in_tot & 0xff,
716 (int)(in_tot >> 8) & 0xff,
717 (int)(in_tot >> 16) & 0xff,
718 (int)(in_tot >> 24) & 0xff);
719 if (i != 8)
720 maybe_err("snprintf");
721 if (write(out, outbufp, i) != i) {
722 maybe_warn("write");
723 in_tot = -1;
724 } else
725 out_tot += i;
726
727out:
728 if (inbufp != NULL)
729 free(inbufp);
730 if (outbufp != NULL)
731 free(outbufp);
732 if (gsizep)
733 *gsizep = out_tot;
734 return in_tot;
735}
736
737/*
738 * uncompress input to output then close the input. return the
739 * uncompressed size written, and put the compressed sized read
740 * into `*gsizep'.
741 */
742static off_t
743gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
744 const char *filename)
745{
746 z_stream z;
747 char *outbufp, *inbufp;
748 off_t out_tot = -1, in_tot = 0;
749 uint32_t out_sub_tot = 0;
750 enum {
751 GZSTATE_MAGIC0,
752 GZSTATE_MAGIC1,
753 GZSTATE_METHOD,
754 GZSTATE_FLAGS,
755 GZSTATE_SKIPPING,
756 GZSTATE_EXTRA,
757 GZSTATE_EXTRA2,
758 GZSTATE_EXTRA3,
759 GZSTATE_ORIGNAME,
760 GZSTATE_COMMENT,
761 GZSTATE_HEAD_CRC1,
762 GZSTATE_HEAD_CRC2,
763 GZSTATE_INIT,
764 GZSTATE_READ,
765 GZSTATE_CRC,
766 GZSTATE_LEN,
767 } state = GZSTATE_MAGIC0;
768 int flags = 0, skip_count = 0;
769 int error = Z_STREAM_ERROR, done_reading = 0;
770 uLong crc = 0;
771 ssize_t wr;
772 int needmore = 0;
773
774#define ADVANCE() { z.next_in++; z.avail_in--; }
775
776 if ((outbufp = malloc(BUFLEN)) == NULL) {
777 maybe_err("malloc failed");
778 goto out2;
779 }
780 if ((inbufp = malloc(BUFLEN)) == NULL) {
781 maybe_err("malloc failed");
782 goto out1;
783 }
784
785 memset(&z, 0, sizeof z);
786 z.avail_in = prelen;
787 z.next_in = (unsigned char *)pre;
788 z.avail_out = BUFLEN;
789 z.next_out = (unsigned char *)outbufp;
790 z.zalloc = NULL;
791 z.zfree = NULL;
792 z.opaque = 0;
793
794 in_tot = prelen;
795 out_tot = 0;
796
797 for (;;) {
798 if ((z.avail_in == 0 || needmore) && done_reading == 0) {
799 ssize_t in_size;
800
801 if (z.avail_in > 0) {
802 memmove(inbufp, z.next_in, z.avail_in);
803 }
804 z.next_in = (unsigned char *)inbufp;
805 in_size = read(in, z.next_in + z.avail_in,
806 BUFLEN - z.avail_in);
807
808 if (in_size == -1) {
809 maybe_warn("failed to read stdin");
810 goto stop_and_fail;
811 } else if (in_size == 0) {
812 done_reading = 1;
813 }
814
815 z.avail_in += in_size;
816 needmore = 0;
817
818 in_tot += in_size;
819 }
820 if (z.avail_in == 0) {
821 if (done_reading && state != GZSTATE_MAGIC0) {
822 maybe_warnx("%s: unexpected end of file",
823 filename);
824 goto stop_and_fail;
825 }
826 goto stop;
827 }
828 switch (state) {
829 case GZSTATE_MAGIC0:
830 if (*z.next_in != GZIP_MAGIC0) {
831 if (in_tot > 0) {
832 maybe_warnx("%s: trailing garbage "
833 "ignored", filename);
834 goto stop;
835 }
836 maybe_warnx("input not gziped (MAGIC0)");
837 goto stop_and_fail;
838 }
839 ADVANCE();
840 state++;
841 out_sub_tot = 0;
842 crc = crc32(0L, Z_NULL, 0);
843 break;
844
845 case GZSTATE_MAGIC1:
846 if (*z.next_in != GZIP_MAGIC1 &&
847 *z.next_in != GZIP_OMAGIC1) {
848 maybe_warnx("input not gziped (MAGIC1)");
849 goto stop_and_fail;
850 }
851 ADVANCE();
852 state++;
853 break;
854
855 case GZSTATE_METHOD:
856 if (*z.next_in != Z_DEFLATED) {
857 maybe_warnx("unknown compression method");
858 goto stop_and_fail;
859 }
860 ADVANCE();
861 state++;
862 break;
863
864 case GZSTATE_FLAGS:
865 flags = *z.next_in;
866 ADVANCE();
867 skip_count = 6;
868 state++;
869 break;
870
871 case GZSTATE_SKIPPING:
872 if (skip_count > 0) {
873 skip_count--;
874 ADVANCE();
875 } else
876 state++;
877 break;
878
879 case GZSTATE_EXTRA:
880 if ((flags & EXTRA_FIELD) == 0) {
881 state = GZSTATE_ORIGNAME;
882 break;
883 }
884 skip_count = *z.next_in;
885 ADVANCE();
886 state++;
887 break;
888
889 case GZSTATE_EXTRA2:
890 skip_count |= ((*z.next_in) << 8);
891 ADVANCE();
892 state++;
893 break;
894
895 case GZSTATE_EXTRA3:
896 if (skip_count > 0) {
897 skip_count--;
898 ADVANCE();
899 } else
900 state++;
901 break;
902
903 case GZSTATE_ORIGNAME:
904 if ((flags & ORIG_NAME) == 0) {
905 state++;
906 break;
907 }
908 if (*z.next_in == 0)
909 state++;
910 ADVANCE();
911 break;
912
913 case GZSTATE_COMMENT:
914 if ((flags & COMMENT) == 0) {
915 state++;
916 break;
917 }
918 if (*z.next_in == 0)
919 state++;
920 ADVANCE();
921 break;
922
923 case GZSTATE_HEAD_CRC1:
924 if (flags & HEAD_CRC)
925 skip_count = 2;
926 else
927 skip_count = 0;
928 state++;
929 break;
930
931 case GZSTATE_HEAD_CRC2:
932 if (skip_count > 0) {
933 skip_count--;
934 ADVANCE();
935 } else
936 state++;
937 break;
938
939 case GZSTATE_INIT:
940 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
941 maybe_warnx("failed to inflateInit");
942 goto stop_and_fail;
943 }
944 state++;
945 break;
946
947 case GZSTATE_READ:
948 error = inflate(&z, Z_FINISH);
949 switch (error) {
950 /* Z_BUF_ERROR goes with Z_FINISH... */
951 case Z_BUF_ERROR:
952 if (z.avail_out > 0 && !done_reading)
953 continue;
954
955 case Z_STREAM_END:
956 case Z_OK:
957 break;
958
959 case Z_NEED_DICT:
960 maybe_warnx("Z_NEED_DICT error");
961 goto stop_and_fail;
962 case Z_DATA_ERROR:
963 maybe_warnx("data stream error");
964 goto stop_and_fail;
965 case Z_STREAM_ERROR:
966 maybe_warnx("internal stream error");
967 goto stop_and_fail;
968 case Z_MEM_ERROR:
969 maybe_warnx("memory allocation error");
970 goto stop_and_fail;
971
972 default:
973 maybe_warn("unknown error from inflate(): %d",
974 error);
975 }
976 wr = BUFLEN - z.avail_out;
977
978 if (wr != 0) {
979 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
980 if (
981#ifndef SMALL
982 /* don't write anything with -t */
983 tflag == 0 &&
984#endif
985 write(out, outbufp, wr) != wr) {
986 maybe_warn("error writing to output");
987 goto stop_and_fail;
988 }
989
990 out_tot += wr;
991 out_sub_tot += wr;
992 }
993
994 if (error == Z_STREAM_END) {
995 inflateEnd(&z);
996 state++;
997 }
998
999 z.next_out = (unsigned char *)outbufp;
1000 z.avail_out = BUFLEN;
1001
1002 break;
1003 case GZSTATE_CRC:
1004 {
1005 uLong origcrc;
1006
1007 if (z.avail_in < 4) {
1008 if (!done_reading) {
1009 needmore = 1;
1010 continue;
1011 }
1012 maybe_warnx("truncated input");
1013 goto stop_and_fail;
1014 }
1015 origcrc = ((unsigned)z.next_in[0] & 0xff) |
1016 ((unsigned)z.next_in[1] & 0xff) << 8 |
1017 ((unsigned)z.next_in[2] & 0xff) << 16 |
1018 ((unsigned)z.next_in[3] & 0xff) << 24;
1019 if (origcrc != crc) {
1020 maybe_warnx("invalid compressed"
1021 " data--crc error");
1022 goto stop_and_fail;
1023 }
1024 }
1025
1026 z.avail_in -= 4;
1027 z.next_in += 4;
1028
1029 if (!z.avail_in && done_reading) {
1030 goto stop;
1031 }
1032 state++;
1033 break;
1034 case GZSTATE_LEN:
1035 {
1036 uLong origlen;
1037
1038 if (z.avail_in < 4) {
1039 if (!done_reading) {
1040 needmore = 1;
1041 continue;
1042 }
1043 maybe_warnx("truncated input");
1044 goto stop_and_fail;
1045 }
1046 origlen = ((unsigned)z.next_in[0] & 0xff) |
1047 ((unsigned)z.next_in[1] & 0xff) << 8 |
1048 ((unsigned)z.next_in[2] & 0xff) << 16 |
1049 ((unsigned)z.next_in[3] & 0xff) << 24;
1050
1051 if (origlen != out_sub_tot) {
1052 maybe_warnx("invalid compressed"
1053 " data--length error");
1054 goto stop_and_fail;
1055 }
1056 }
1057
1058 z.avail_in -= 4;
1059 z.next_in += 4;
1060
1061 if (error < 0) {
1062 maybe_warnx("decompression error");
1063 goto stop_and_fail;
1064 }
1065 state = GZSTATE_MAGIC0;
1066 break;
1067 }
1068 continue;
1069stop_and_fail:
1070 out_tot = -1;
1071stop:
1072 break;
1073 }
1074 if (state > GZSTATE_INIT)
1075 inflateEnd(&z);
1076
1077 free(inbufp);
1078out1:
1079 free(outbufp);
1080out2:
1081 if (gsizep)
1082 *gsizep = in_tot;
1083 return (out_tot);
1084}
1085
1086#ifndef SMALL
1087/*
1088 * set the owner, mode, flags & utimes using the given file descriptor.
1089 * file is only used in possible warning messages.
1090 */
1091static void
1092copymodes(int fd, const struct stat *sbp, const char *file)
1093{
1094 struct timeval times[2];
1095 struct stat sb;
1096
1097 /*
1098 * If we have no info on the input, give this file some
1099 * default values and return..
1100 */
1101 if (sbp == NULL) {
1102 mode_t mask = umask(022);
1103
1104 (void)fchmod(fd, DEFFILEMODE & ~mask);
1105 (void)umask(mask);
1106 return;
1107 }
1108 sb = *sbp;
1109
1110 /* if the chown fails, remove set-id bits as-per compress(1) */
1111 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1112 if (errno != EPERM)
1113 maybe_warn("couldn't fchown: %s", file);
1114 sb.st_mode &= ~(S_ISUID|S_ISGID);
1115 }
1116
1117 /* we only allow set-id and the 9 normal permission bits */
1118 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1119 if (fchmod(fd, sb.st_mode) < 0)
1120 maybe_warn("couldn't fchmod: %s", file);
1121
1122#ifdef __APPLE__
1123 TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atimespec);
1124 TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtimespec);
1125#else
1126 TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atim);
1127 TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtim);
1128#endif
1129 if (futimes(fd, times) < 0)
1130 maybe_warn("couldn't utimes: %s", file);
1131
1132 /* only try flags if they exist already */
1133 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1134 maybe_warn("couldn't fchflags: %s", file);
1135}
1136#endif
1137
1138/* what sort of file is this? */
1139static enum filetype
1140file_gettype(u_char *buf)
1141{
1142
1143 if (buf[0] == GZIP_MAGIC0 &&
1144 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1145 return FT_GZIP;
1146 else
1147#ifndef NO_BZIP2_SUPPORT
1148 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1149 buf[3] >= '0' && buf[3] <= '9')
1150 return FT_BZIP2;
1151 else
1152#endif
1153#ifndef NO_COMPRESS_SUPPORT
1154 if (memcmp(buf, Z_MAGIC, 2) == 0)
1155 return FT_Z;
1156 else
1157#endif
1158#ifndef NO_PACK_SUPPORT
1159 if (memcmp(buf, PACK_MAGIC, 2) == 0)
1160 return FT_PACK;
1161 else
1162#endif
1163#ifndef NO_XZ_SUPPORT
1164 if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */
1165 return FT_XZ;
1166 else
1167#endif
1168 return FT_UNKNOWN;
1169}
1170
1171#ifndef SMALL
1172/* check the outfile is OK. */
1173static int
1174check_outfile(const char *outfile)
1175{
1176 struct stat sb;
1177 int ok = 1;
1178
1179 if (lflag == 0 && stat(outfile, &sb) == 0) {
1180 if (fflag)
1181 unlink(outfile);
1182 else if (isatty(STDIN_FILENO)) {
1183 char ans[10] = { 'n', '\0' }; /* default */
1184
1185 fprintf(stderr, "%s already exists -- do you wish to "
1186 "overwrite (y or n)? " , outfile);
1187 (void)fgets(ans, sizeof(ans) - 1, stdin);
1188 if (ans[0] != 'y' && ans[0] != 'Y') {
1189 fprintf(stderr, "\tnot overwriting\n");
1190 ok = 0;
1191 } else
1192 unlink(outfile);
1193 } else {
1194 maybe_warnx("%s already exists -- skipping", outfile);
1195 ok = 0;
1196 }
1197 }
1198 return ok;
1199}
1200
1201static void
1202unlink_input(const char *file, const struct stat *sb)
1203{
1204 struct stat nsb;
1205
1206 if (kflag)
1207 return;
1208 bzero(&nsb, sizeof(nsb));
1209 if (stat(file, &nsb) != 0)
1210 /* Must be gone already */
1211 return;
1212 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1213 /* Definitely a different file */
1214 return;
1215 unlink(file);
1216}
1217
1218static void
1219sigint_handler(int signo __unused)
1220{
1221
1222 if (remove_file != NULL)
1223 unlink(remove_file);
1224 _exit(2);
1225}
1226#endif
1227
1228static const suffixes_t *
1229check_suffix(char *file, int xlate)
1230{
1231 const suffixes_t *s;
1232 int len = strlen(file);
1233 char *sp;
1234
1235 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1236 /* if it doesn't fit in "a.suf", don't bother */
1237 if (s->ziplen >= len)
1238 continue;
1239 sp = file + len - s->ziplen;
1240 if (strcmp(s->zipped, sp) != 0)
1241 continue;
1242 if (xlate)
1243 strcpy(sp, s->normal);
1244 return s;
1245 }
1246 return NULL;
1247}
1248
1249#ifdef __APPLE__
1250static void
1251clear_type_and_creator(int fd)
1252{
1253 struct attrlist alist;
1254 struct {
1255 u_int32_t length;
1256 char info[32];
1257 } abuf;
1258
1259 memset(&alist, 0, sizeof(alist));
1260 alist.bitmapcount = ATTR_BIT_MAP_COUNT;
1261 alist.commonattr = ATTR_CMN_FNDRINFO;
1262
1263 if (!fgetattrlist(fd, &alist, &abuf, sizeof(abuf), 0) && abuf.length == sizeof(abuf)) {
1264 memset(abuf.info, 0, 8);
1265 fsetattrlist(fd, &alist, abuf.info, sizeof(abuf.info), 0);
1266 }
1267}
1268#endif /* __APPLE__ */
1269
1270/*
1271 * compress the given file: create a corresponding .gz file and remove the
1272 * original.
1273 */
1274static off_t
1275file_compress(char *file, char *outfile, size_t outsize)
1276{
1277 int in;
1278 int out;
1279 off_t size, insize;
1280#ifndef SMALL
1281 struct stat isb, osb;
1282 const suffixes_t *suff;
1283#endif
1284
1285 in = open(file, O_RDONLY);
1286 if (in == -1) {
1287 maybe_warn("can't open %s", file);
1288 return (-1);
1289 }
1290
1291#ifndef SMALL
1292 bzero(&isb, sizeof(isb));
1293 if (fstat(in, &isb) != 0) {
1294 maybe_warn("couldn't stat: %s", file);
1295 close(in);
1296 return (-1);
1297 }
1298#endif
1299
1300 if (cflag == 0) {
1301#ifndef SMALL
1302 if (isb.st_nlink > 1 && fflag == 0) {
1303 maybe_warnx("%s has %d other link%s -- skipping",
1304 file, isb.st_nlink - 1,
1305 (isb.st_nlink - 1) == 1 ? "" : "s");
1306 close(in);
1307 return (-1);
1308 }
1309
1310 if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1311 suff->zipped[0] != 0) {
1312 maybe_warnx("%s already has %s suffix -- unchanged",
1313 file, suff->zipped);
1314 close(in);
1315 return (-1);
1316 }
1317#endif
1318
1319 /* Add (usually) .gz to filename */
1320 if ((size_t)snprintf(outfile, outsize, "%s%s",
1321 file, suffixes[0].zipped) >= outsize)
1322 memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1323 suffixes[0].zipped, suffixes[0].ziplen + 1);
1324
1325#ifndef SMALL
1326 if (check_outfile(outfile) == 0) {
1327 close(in);
1328 return (-1);
1329 }
1330#endif
1331 }
1332
1333 if (cflag == 0) {
1334 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1335 if (out == -1) {
1336 maybe_warn("could not create output: %s", outfile);
1337 fclose(stdin);
1338 return (-1);
1339 }
1340#ifndef SMALL
1341 remove_file = outfile;
1342#endif
1343 } else
1344 out = STDOUT_FILENO;
1345
1346 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1347
1348#ifndef __APPLE__
1349 (void)close(in);
1350#endif /* !__APPLE__ */
1351
1352 /*
1353 * If there was an error, insize will be -1.
1354 * If we compressed to stdout, just return the size.
1355 * Otherwise stat the file and check it is the correct size.
1356 * We only blow away the file if we can stat the output and it
1357 * has the expected size.
1358 */
1359 if (cflag != 0)
1360 return (insize == -1 ? -1 : size);
1361
1362#ifndef SMALL
1363 if (fstat(out, &osb) != 0) {
1364 maybe_warn("couldn't stat: %s", outfile);
1365 goto bad_outfile;
1366 }
1367
1368 if (osb.st_size != size) {
1369 maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1370 outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1371 goto bad_outfile;
1372 }
1373
1374#ifdef __APPLE__
1375 fcopyfile(in, out, 0, COPYFILE_ACL | COPYFILE_XATTR);
1376 clear_type_and_creator(out);
1377#endif /* __APPLE__ */
1378 copymodes(out, &isb, outfile);
1379 remove_file = NULL;
1380#endif
1381#ifdef __APPLE__
1382 (void)close(in);
1383#endif /* __APPLE__ */
1384 if (close(out) == -1)
1385 maybe_warn("couldn't close output");
1386
1387 /* output is good, ok to delete input */
1388 unlink_input(file, &isb);
1389 return (size);
1390
1391#ifndef SMALL
1392 bad_outfile:
1393 if (close(out) == -1)
1394 maybe_warn("couldn't close output");
1395
1396 maybe_warnx("leaving original %s", file);
1397 unlink(outfile);
1398 return (size);
1399#endif
1400}
1401
1402/* uncompress the given file and remove the original */
1403static off_t
1404file_uncompress(char *file, char *outfile, size_t outsize)
1405{
1406 struct stat isb, osb;
1407 off_t size;
1408 ssize_t rbytes;
1409 unsigned char header1[4];
1410 enum filetype method;
1411 int fd, ofd, zfd = -1;
1412#ifndef SMALL
1413 ssize_t rv;
1414 time_t timestamp = 0;
1415 unsigned char name[PATH_MAX + 1];
1416#endif
1417
1418 /* gather the old name info */
1419
1420 fd = open(file, O_RDONLY);
1421 if (fd < 0) {
1422 maybe_warn("can't open %s", file);
1423 goto lose;
1424 }
1425
1426 strlcpy(outfile, file, outsize);
1427 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1428 maybe_warnx("%s: unknown suffix -- ignored", file);
1429 goto lose;
1430 }
1431
1432 rbytes = read(fd, header1, sizeof header1);
1433 if (rbytes != sizeof header1) {
1434 /* we don't want to fail here. */
1435#ifndef SMALL
1436 if (fflag)
1437 goto lose;
1438#endif
1439 if (rbytes == -1)
1440 maybe_warn("can't read %s", file);
1441 else
1442 goto unexpected_EOF;
1443 goto lose;
1444 }
1445
1446 method = file_gettype(header1);
1447#ifndef SMALL
1448 if (fflag == 0 && method == FT_UNKNOWN) {
1449 maybe_warnx("%s: not in gzip format", file);
1450 goto lose;
1451 }
1452
1453#endif
1454
1455#ifndef SMALL
1456 if (method == FT_GZIP && Nflag) {
1457 unsigned char ts[4]; /* timestamp */
1458
1459 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1460 if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1461 goto unexpected_EOF;
1462 if (rv == -1) {
1463 if (!fflag)
1464 maybe_warn("can't read %s", file);
1465 goto lose;
1466 }
1467 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1468
1469 if (header1[3] & ORIG_NAME) {
1470 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1471 if (rbytes < 0) {
1472 maybe_warn("can't read %s", file);
1473 goto lose;
1474 }
1475 if (name[0] != 0) {
1476 /* preserve original directory name */
1477 char *dp = strrchr(file, '/');
1478 if (dp == NULL)
1479 dp = file;
1480 else
1481 dp++;
1482 snprintf(outfile, outsize, "%.*s%.*s",
1483 (int) (dp - file),
1484 file, (int) rbytes, name);
1485 }
1486 }
1487 }
1488#endif
1489 lseek(fd, 0, SEEK_SET);
1490 bzero(&isb, sizeof(isb));
1491 if (cflag == 0 || lflag) {
1492 if (fstat(fd, &isb) != 0)
1493 goto lose;
1494#ifndef SMALL
1495 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1496 maybe_warnx("%s has %d other links -- skipping",
1497 file, isb.st_nlink - 1);
1498 goto lose;
1499 }
1500 if (nflag == 0 && timestamp)
1501 isb.st_mtime = timestamp;
1502 if (check_outfile(outfile) == 0)
1503 goto lose;
1504#endif
1505 }
1506
1507 if (cflag == 0 && lflag == 0) {
1508 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1509 if (zfd == STDOUT_FILENO) {
1510 /* We won't close STDOUT_FILENO later... */
1511 zfd = dup(zfd);
1512 close(STDOUT_FILENO);
1513 }
1514 if (zfd == -1) {
1515 maybe_warn("can't open %s", outfile);
1516 goto lose;
1517 }
1518#ifndef SMALL
1519 remove_file = outfile;
1520#endif
1521 } else
1522 zfd = STDOUT_FILENO;
1523
1524 switch (method) {
1525#ifndef NO_BZIP2_SUPPORT
1526 case FT_BZIP2:
1527 /* XXX */
1528 if (lflag) {
1529 maybe_warnx("no -l with bzip2 files");
1530 goto lose;
1531 }
1532
1533 size = unbzip2(fd, zfd, NULL, 0, NULL);
1534 break;
1535#endif
1536
1537#ifndef NO_COMPRESS_SUPPORT
1538 case FT_Z: {
1539 FILE *in, *out;
1540
1541 /* XXX */
1542 if (lflag) {
1543 maybe_warnx("no -l with Lempel-Ziv files");
1544 goto lose;
1545 }
1546
1547 if ((in = zdopen(fd)) == NULL) {
1548 maybe_warn("zdopen for read: %s", file);
1549 goto lose;
1550 }
1551
1552 out = fdopen(dup(zfd), "w");
1553 if (out == NULL) {
1554 maybe_warn("fdopen for write: %s", outfile);
1555 fclose(in);
1556 goto lose;
1557 }
1558
1559 size = zuncompress(in, out, NULL, 0, NULL);
1560 /* need to fclose() if ferror() is true... */
1561 if (ferror(in) | fclose(in)) {
1562 maybe_warn("failed infile fclose");
1563 unlink(outfile);
1564 (void)fclose(out);
1565 }
1566 if (fclose(out) != 0) {
1567 maybe_warn("failed outfile fclose");
1568 unlink(outfile);
1569 goto lose;
1570 }
1571 break;
1572 }
1573#endif
1574
1575#ifndef NO_PACK_SUPPORT
1576 case FT_PACK:
1577 if (lflag) {
1578 maybe_warnx("no -l with packed files");
1579 goto lose;
1580 }
1581
1582 size = unpack(fd, zfd, NULL, 0, NULL);
1583 break;
1584#endif
1585
1586#ifndef NO_XZ_SUPPORT
1587 case FT_XZ:
1588 if (lflag) {
1589 maybe_warnx("no -l with xz files");
1590 goto lose;
1591 }
1592
1593 size = unxz(fd, zfd, NULL, 0, NULL);
1594 break;
1595#endif
1596
1597#ifndef SMALL
1598 case FT_UNKNOWN:
1599 if (lflag) {
1600 maybe_warnx("no -l for unknown filetypes");
1601 goto lose;
1602 }
1603 size = cat_fd(NULL, 0, NULL, fd);
1604 break;
1605#endif
1606 default:
1607 if (lflag) {
1608 print_list(fd, isb.st_size, outfile, isb.st_mtime);
1609 close(fd);
1610 return -1; /* XXX */
1611 }
1612
1613 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1614 break;
1615 }
1616
1617 if (close(fd) != 0)
1618 maybe_warn("couldn't close input");
1619 if (zfd != STDOUT_FILENO && close(zfd) != 0)
1620 maybe_warn("couldn't close output");
1621
1622 if (size == -1) {
1623 if (cflag == 0)
1624 unlink(outfile);
1625 maybe_warnx("%s: uncompress failed", file);
1626 return -1;
1627 }
1628
1629 /* if testing, or we uncompressed to stdout, this is all we need */
1630#ifndef SMALL
1631 if (tflag)
1632 return size;
1633#endif
1634 /* if we are uncompressing to stdin, don't remove the file. */
1635 if (cflag)
1636 return size;
1637
1638 /*
1639 * if we create a file...
1640 */
1641 /*
1642 * if we can't stat the file don't remove the file.
1643 */
1644
1645 ofd = open(outfile, O_RDWR, 0);
1646 if (ofd == -1) {
1647 maybe_warn("couldn't open (leaving original): %s",
1648 outfile);
1649 return -1;
1650 }
1651 if (fstat(ofd, &osb) != 0) {
1652 maybe_warn("couldn't stat (leaving original): %s",
1653 outfile);
1654 close(ofd);
1655 return -1;
1656 }
1657 if (osb.st_size != size) {
1658 maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1659 (uintmax_t)size, (uintmax_t)osb.st_size);
1660 close(ofd);
1661 unlink(outfile);
1662 return -1;
1663 }
1664#ifndef SMALL
1665 copymodes(ofd, &isb, outfile);
1666 remove_file = NULL;
1667#endif
1668 close(ofd);
1669 unlink_input(file, &isb);
1670 return size;
1671
1672 unexpected_EOF:
1673 maybe_warnx("%s: unexpected end of file", file);
1674 lose:
1675 if (fd != -1)
1676 close(fd);
1677 if (zfd != -1 && zfd != STDOUT_FILENO)
1678 close(fd);
1679 return -1;
1680}
1681
1682#ifndef SMALL
1683static off_t
1684cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1685{
1686 char buf[BUFLEN];
1687 off_t in_tot;
1688 ssize_t w;
1689
1690 in_tot = count;
1691 w = write(STDOUT_FILENO, prepend, count);
1692 if (w == -1 || (size_t)w != count) {
1693 maybe_warn("write to stdout");
1694 return -1;
1695 }
1696 for (;;) {
1697 ssize_t rv;
1698
1699 rv = read(fd, buf, sizeof buf);
1700 if (rv == 0)
1701 break;
1702 if (rv < 0) {
1703 maybe_warn("read from fd %d", fd);
1704 break;
1705 }
1706
1707 if (write(STDOUT_FILENO, buf, rv) != rv) {
1708 maybe_warn("write to stdout");
1709 break;
1710 }
1711 in_tot += rv;
1712 }
1713
1714 if (gsizep)
1715 *gsizep = in_tot;
1716 return (in_tot);
1717}
1718#endif
1719
1720static void
1721handle_stdin(void)
1722{
1723 unsigned char header1[4];
1724 off_t usize, gsize;
1725 enum filetype method;
1726 ssize_t bytes_read;
1727#ifndef NO_COMPRESS_SUPPORT
1728 FILE *in;
1729#endif
1730
1731#ifndef SMALL
1732 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1733 maybe_warnx("standard input is a terminal -- ignoring");
1734 return;
1735 }
1736#endif
1737
1738 if (lflag) {
1739 struct stat isb;
1740
1741 /* XXX could read the whole file, etc. */
1742 if (fstat(STDIN_FILENO, &isb) < 0) {
1743 maybe_warn("fstat");
1744 return;
1745 }
1746 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1747 return;
1748 }
1749
1750 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1751 if (bytes_read == -1) {
1752 maybe_warn("can't read stdin");
1753 return;
1754 } else if (bytes_read != sizeof(header1)) {
1755 maybe_warnx("(stdin): unexpected end of file");
1756 return;
1757 }
1758
1759 method = file_gettype(header1);
1760 switch (method) {
1761 default:
1762#ifndef SMALL
1763 if (fflag == 0) {
1764 maybe_warnx("unknown compression format");
1765 return;
1766 }
1767 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1768 break;
1769#endif
1770 case FT_GZIP:
1771 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1772 (char *)header1, sizeof header1, &gsize, "(stdin)");
1773 break;
1774#ifndef NO_BZIP2_SUPPORT
1775 case FT_BZIP2:
1776 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1777 (char *)header1, sizeof header1, &gsize);
1778 break;
1779#endif
1780#ifndef NO_COMPRESS_SUPPORT
1781 case FT_Z:
1782 if ((in = zdopen(STDIN_FILENO)) == NULL) {
1783 maybe_warnx("zopen of stdin");
1784 return;
1785 }
1786
1787 usize = zuncompress(in, stdout, (char *)header1,
1788 sizeof header1, &gsize);
1789 fclose(in);
1790 break;
1791#endif
1792#ifndef NO_PACK_SUPPORT
1793 case FT_PACK:
1794 usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1795 (char *)header1, sizeof header1, &gsize);
1796 break;
1797#endif
1798#ifndef NO_XZ_SUPPORT
1799 case FT_XZ:
1800 usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1801 (char *)header1, sizeof header1, &gsize);
1802 break;
1803#endif
1804 }
1805
1806#ifndef SMALL
1807 if (vflag && !tflag && usize != -1 && gsize != -1)
1808 print_verbage(NULL, NULL, usize, gsize);
1809 if (vflag && tflag)
1810 print_test("(stdin)", usize != -1);
1811#endif
1812
1813}
1814
1815static void
1816handle_stdout(void)
1817{
1818 off_t gsize, usize;
1819 struct stat sb;
1820 time_t systime;
1821 uint32_t mtime;
1822 int ret;
1823
1824#ifndef SMALL
1825 if (fflag == 0 && isatty(STDOUT_FILENO)) {
1826 maybe_warnx("standard output is a terminal -- ignoring");
1827 return;
1828 }
1829#endif
1830 /* If stdin is a file use it's mtime, otherwise use current time */
1831 ret = fstat(STDIN_FILENO, &sb);
1832
1833#ifndef SMALL
1834 if (ret < 0) {
1835 maybe_warn("Can't stat stdin");
1836 return;
1837 }
1838#endif
1839
1840 if (S_ISREG(sb.st_mode))
1841 mtime = (uint32_t)sb.st_mtime;
1842 else {
1843 systime = time(NULL);
1844#ifndef SMALL
1845 if (systime == -1) {
1846 maybe_warn("time");
1847 return;
1848 }
1849#endif
1850 mtime = (uint32_t)systime;
1851 }
1852
1853 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1854#ifndef SMALL
1855 if (vflag && !tflag && usize != -1 && gsize != -1)
1856 print_verbage(NULL, NULL, usize, gsize);
1857#endif
1858}
1859
1860/* do what is asked for, for the path name */
1861static void
1862handle_pathname(char *path)
1863{
1864 char *opath = path, *s = NULL;
1865 ssize_t len;
1866 int slen;
1867 struct stat sb;
1868
1869 /* check for stdout/stdin */
1870 if (path[0] == '-' && path[1] == '\0') {
1871 if (dflag)
1872 handle_stdin();
1873 else
1874 handle_stdout();
1875 return;
1876 }
1877
1878#ifdef __APPLE__
1879 if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) {
1880 char *suffix = strrchr(path, '.');
1881 if (suffix == NULL || strcmp(suffix, Z_SUFFIX) != 0) {
1882 len = strlen(path);
1883 slen = sizeof(Z_SUFFIX) - 1;
1884 s = malloc(len + slen + 1);
1885 memcpy(s, path, len);
1886 memcpy(s + len, Z_SUFFIX, slen + 1);
1887 path = s;
1888 }
1889 }
1890#endif
1891
1892retry:
1893 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1894 lstat(path, &sb) != 0)) {
1895 /* lets try <path>.gz if we're decompressing */
1896 if (dflag && s == NULL && errno == ENOENT) {
1897 len = strlen(path);
1898 slen = suffixes[0].ziplen;
1899 s = malloc(len + slen + 1);
1900 if (s == NULL)
1901 maybe_err("malloc");
1902 memcpy(s, path, len);
1903 memcpy(s + len, suffixes[0].zipped, slen + 1);
1904 path = s;
1905 goto retry;
1906 }
1907#ifdef __APPLE__
1908 /* Include actual path for clarity. */
1909 maybe_warn("can't stat: %s (%s)", opath, path);
1910#else
1911 maybe_warn("can't stat: %s", opath);
1912#endif
1913 goto out;
1914 }
1915
1916 if (S_ISDIR(sb.st_mode)) {
1917#ifndef SMALL
1918 if (rflag)
1919 handle_dir(path);
1920 else
1921#endif
1922 maybe_warnx("%s is a directory", path);
1923 goto out;
1924 }
1925
1926 if (S_ISREG(sb.st_mode))
1927 handle_file(path, &sb);
1928 else
1929 maybe_warnx("%s is not a regular file", path);
1930
1931out:
1932 if (s)
1933 free(s);
1934}
1935
1936/* compress/decompress a file */
1937static void
1938handle_file(char *file, struct stat *sbp)
1939{
1940 off_t usize, gsize;
1941 char outfile[PATH_MAX];
1942
1943 infile = file;
1944 if (dflag) {
1945 usize = file_uncompress(file, outfile, sizeof(outfile));
1946#ifndef SMALL
1947 if (vflag && tflag)
1948 print_test(file, usize != -1);
1949#endif
1950 if (usize == -1)
1951 return;
1952 gsize = sbp->st_size;
1953 } else {
1954 gsize = file_compress(file, outfile, sizeof(outfile));
1955 if (gsize == -1)
1956 return;
1957 usize = sbp->st_size;
1958 }
1959
1960
1961#ifndef SMALL
1962 if (vflag && !tflag)
1963 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1964#endif
1965}
1966
1967#ifndef SMALL
1968/* this is used with -r to recursively descend directories */
1969static void
1970handle_dir(char *dir)
1971{
1972 char *path_argv[2];
1973 FTS *fts;
1974 FTSENT *entry;
1975
1976 path_argv[0] = dir;
1977 path_argv[1] = 0;
1978 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1979 if (fts == NULL) {
1980 warn("couldn't fts_open %s", dir);
1981 return;
1982 }
1983
1984 while ((entry = fts_read(fts))) {
1985 switch(entry->fts_info) {
1986 case FTS_D:
1987 case FTS_DP:
1988 continue;
1989
1990 case FTS_DNR:
1991 case FTS_ERR:
1992 case FTS_NS:
1993 maybe_warn("%s", entry->fts_path);
1994 continue;
1995 case FTS_F:
1996 handle_file(entry->fts_path, entry->fts_statp);
1997 }
1998 }
1999 (void)fts_close(fts);
2000}
2001#endif
2002
2003/* print a ratio - size reduction as a fraction of uncompressed size */
2004static void
2005print_ratio(off_t in, off_t out, FILE *where)
2006{
2007 int percent10; /* 10 * percent */
2008 off_t diff;
2009 char buff[8];
2010 int len;
2011
2012 diff = in - out/2;
2013 if (diff <= 0)
2014 /*
2015 * Output is more than double size of input! print -99.9%
2016 * Quite possibly we've failed to get the original size.
2017 */
2018 percent10 = -999;
2019 else {
2020 /*
2021 * We only need 12 bits of result from the final division,
2022 * so reduce the values until a 32bit division will suffice.
2023 */
2024 while (in > 0x100000) {
2025 diff >>= 1;
2026 in >>= 1;
2027 }
2028 if (in != 0)
2029 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2030 else
2031 percent10 = 0;
2032 }
2033
2034 len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2035 /* Move the '.' to before the last digit */
2036 buff[len - 1] = buff[len - 2];
2037 buff[len - 2] = '.';
2038 fprintf(where, "%5s%%", buff);
2039}
2040
2041#ifndef SMALL
2042/* print compression statistics, and the new name (if there is one!) */
2043static void
2044print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
2045{
2046 if (file)
2047 fprintf(stderr, "%s:%s ", file,
2048 strlen(file) < 7 ? "\t\t" : "\t");
2049 print_ratio(usize, gsize, stderr);
2050 if (nfile)
2051 fprintf(stderr, " -- replaced with %s", nfile);
2052 fprintf(stderr, "\n");
2053 fflush(stderr);
2054}
2055
2056/* print test results */
2057static void
2058print_test(const char *file, int ok)
2059{
2060
2061 if (exit_value == 0 && ok == 0)
2062 exit_value = 1;
2063 fprintf(stderr, "%s:%s %s\n", file,
2064 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2065 fflush(stderr);
2066}
2067#endif
2068
2069/* print a file's info ala --list */
2070/* eg:
2071 compressed uncompressed ratio uncompressed_name
2072 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2073*/
2074static void
2075print_list(int fd, off_t out, const char *outfile, time_t ts)
2076{
2077 static int first = 1;
2078#ifndef SMALL
2079 static off_t in_tot, out_tot;
2080 uint32_t crc = 0;
2081#endif
2082 off_t in = 0, rv;
2083
2084 if (first) {
2085#ifndef SMALL
2086 if (vflag)
2087 printf("method crc date time ");
2088#endif
2089 if (qflag == 0)
2090 printf(" compressed uncompressed "
2091 "ratio uncompressed_name\n");
2092 }
2093 first = 0;
2094
2095 /* print totals? */
2096#ifndef SMALL
2097 if (fd == -1) {
2098 in = in_tot;
2099 out = out_tot;
2100 } else
2101#endif
2102 {
2103 /* read the last 4 bytes - this is the uncompressed size */
2104 rv = lseek(fd, (off_t)(-8), SEEK_END);
2105 if (rv != -1) {
2106 unsigned char buf[8];
2107 uint32_t usize;
2108
2109 rv = read(fd, (char *)buf, sizeof(buf));
2110 if (rv == -1)
2111 maybe_warn("read of uncompressed size");
2112 else if (rv != sizeof(buf))
2113 maybe_warnx("read of uncompressed size");
2114
2115 else {
2116 usize = buf[4] | buf[5] << 8 |
2117 buf[6] << 16 | buf[7] << 24;
2118 in = (off_t)usize;
2119#ifndef SMALL
2120 crc = buf[0] | buf[1] << 8 |
2121 buf[2] << 16 | buf[3] << 24;
2122#endif
2123 }
2124 }
2125 }
2126
2127#ifndef SMALL
2128 if (vflag && fd == -1)
2129 printf(" ");
2130 else if (vflag) {
2131 char *date = ctime(&ts);
2132
2133 /* skip the day, 1/100th second, and year */
2134 date += 4;
2135 date[12] = 0;
2136 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2137 }
2138 in_tot += in;
2139 out_tot += out;
2140#else
2141 (void)&ts; /* XXX */
2142#endif
2143 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2144 print_ratio(in, out, stdout);
2145 printf(" %s\n", outfile);
2146}
2147
2148/* display the usage of NetBSD gzip */
2149static void
2150usage(void)
2151{
2152
2153 fprintf(stderr, "%s\n", gzip_version);
2154 fprintf(stderr,
2155#ifdef SMALL
2156 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2157#else
2158 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2159 " -1 --fast fastest (worst) compression\n"
2160 " -2 .. -8 set compression level\n"
2161 " -9 --best best (slowest) compression\n"
2162 " -c --stdout write to stdout, keep original files\n"
2163 " --to-stdout\n"
2164 " -d --decompress uncompress files\n"
2165 " --uncompress\n"
2166 " -f --force force overwriting & compress links\n"
2167 " -h --help display this help\n"
2168 " -k --keep don't delete input files during operation\n"
2169 " -l --list list compressed file contents\n"
2170 " -N --name save or restore original file name and time stamp\n"
2171 " -n --no-name don't save original file name or time stamp\n"
2172 " -q --quiet output no warnings\n"
2173 " -r --recursive recursively compress files in directories\n"
2174 " -S .suf use suffix .suf instead of .gz\n"
2175 " --suffix .suf\n"
2176 " -t --test test compressed file\n"
2177 " -V --version display program version\n"
2178 " -v --verbose print extra statistics\n",
2179#endif
2180 getprogname());
2181 exit(0);
2182}
2183
2184#ifndef SMALL
2185/* display the license information of FreeBSD gzip */
2186static void
2187display_license(void)
2188{
2189
2190#ifdef __APPLE__
2191 fprintf(stderr, "%s (based on FreeBSD gzip 20111009)\n", gzip_version);
2192#else
2193 fprintf(stderr, "%s (based on NetBSD gzip 20111009)\n", gzip_version);
2194#endif
2195 fprintf(stderr, "%s\n", gzip_copyright);
2196 exit(0);
2197}
2198#endif
2199
2200/* display the version of NetBSD gzip */
2201static void
2202display_version(void)
2203{
2204
2205 fprintf(stderr, "%s\n", gzip_version);
2206 exit(0);
2207}
2208
2209#ifndef NO_BZIP2_SUPPORT
2210#include "unbzip2.c"
2211#endif
2212#ifndef NO_COMPRESS_SUPPORT
2213#include "zuncompress.c"
2214#endif
2215#ifndef NO_PACK_SUPPORT
2216#include "unpack.c"
2217#endif
2218#ifndef NO_XZ_SUPPORT
2219#include "unxz.c"
2220#endif
2221
2222static ssize_t
2223read_retry(int fd, void *buf, size_t sz)
2224{
2225 char *cp = buf;
2226 size_t left = MIN(sz, (size_t) SSIZE_MAX);
2227
2228 while (left > 0) {
2229 ssize_t ret;
2230
2231 ret = read(fd, cp, left);
2232 if (ret == -1) {
2233 return ret;
2234 } else if (ret == 0) {
2235 break; /* EOF */
2236 }
2237 cp += ret;
2238 left -= ret;
2239 }
2240
2241 return sz - left;
2242}