1*74b93bffSmartin /* $NetBSD: gzip.c,v 1.127 2024/06/01 10:17:12 martin Exp $ */
24084ec54Smrg
34084ec54Smrg /*
48f36055fSmrg * Copyright (c) 1997-2024 Matthew R. Green
54084ec54Smrg * All rights reserved.
64084ec54Smrg *
74084ec54Smrg * Redistribution and use in source and binary forms, with or without
84084ec54Smrg * modification, are permitted provided that the following conditions
94084ec54Smrg * are met:
104084ec54Smrg * 1. Redistributions of source code must retain the above copyright
114084ec54Smrg * notice, this list of conditions and the following disclaimer.
124084ec54Smrg * 2. Redistributions in binary form must reproduce the above copyright
134084ec54Smrg * notice, this list of conditions and the following disclaimer in the
144084ec54Smrg * documentation and/or other materials provided with the distribution.
154084ec54Smrg *
164084ec54Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
174084ec54Smrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
184084ec54Smrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
194084ec54Smrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
204084ec54Smrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
214084ec54Smrg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
224084ec54Smrg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
234084ec54Smrg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
244084ec54Smrg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254084ec54Smrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264084ec54Smrg * SUCH DAMAGE.
274084ec54Smrg */
284084ec54Smrg
29d8b33778Schristos #if HAVE_NBTOOL_CONFIG_H
30d8b33778Schristos #include "nbtool_config.h"
31d8b33778Schristos #endif
32d8b33778Schristos
3375fa5559Smrg #include <sys/cdefs.h>
3475fa5559Smrg #ifndef lint
358f36055fSmrg __COPYRIGHT("@(#) Copyright (c) 1997-2024 Matthew R. Green. "
368f36055fSmrg "All rights reserved.");
37*74b93bffSmartin __RCSID("$NetBSD: gzip.c,v 1.127 2024/06/01 10:17:12 martin Exp $");
3875fa5559Smrg #endif /* not lint */
3975fa5559Smrg
404084ec54Smrg /*
414084ec54Smrg * gzip.c -- GPL free gzip using zlib.
424084ec54Smrg *
43434117bcSdsl * RFC 1950 covers the zlib format
44434117bcSdsl * RFC 1951 covers the deflate format
45434117bcSdsl * RFC 1952 covers the gzip format
46434117bcSdsl *
4788899e33Smrg * TODO:
48ecadec7fSmrg * - use mmap where possible
497569ff68Swiz * - handle some signals better (remove outfile?)
50a9c239e6Smrg * - make bzip2/compress -v/-t/-l support work as well as possible
514084ec54Smrg */
524084ec54Smrg
534084ec54Smrg #include <sys/param.h>
544084ec54Smrg #include <sys/stat.h>
554084ec54Smrg #include <sys/time.h>
564084ec54Smrg
57d55b8a21Sdsl #include <inttypes.h>
584084ec54Smrg #include <unistd.h>
594084ec54Smrg #include <stdio.h>
604084ec54Smrg #include <string.h>
614084ec54Smrg #include <stdlib.h>
624084ec54Smrg #include <err.h>
634084ec54Smrg #include <errno.h>
644084ec54Smrg #include <fcntl.h>
654084ec54Smrg #include <zlib.h>
664084ec54Smrg #include <fts.h>
674084ec54Smrg #include <libgen.h>
684084ec54Smrg #include <stdarg.h>
694084ec54Smrg #include <getopt.h>
7040c07374Sjmc #include <time.h>
7140ec67d2Schristos #include <paths.h>
724084ec54Smrg
73d55b8a21Sdsl #ifndef PRIdOFF
74d55b8a21Sdsl #define PRIdOFF PRId64
75d55b8a21Sdsl #endif
76d55b8a21Sdsl
77ecadec7fSmrg /* what type of file are we dealing with */
78ecadec7fSmrg enum filetype {
79ecadec7fSmrg FT_GZIP,
80b6e59003Stsutsui #ifndef NO_BZIP2_SUPPORT
81ecadec7fSmrg FT_BZIP2,
82b6e59003Stsutsui #endif
83b6e59003Stsutsui #ifndef NO_COMPRESS_SUPPORT
8491d1fa94Smrg FT_Z,
85b6e59003Stsutsui #endif
86569ceba5Smrg #ifndef NO_PACK_SUPPORT
87569ceba5Smrg FT_PACK,
88569ceba5Smrg #endif
8940b41259Schristos #ifndef NO_XZ_SUPPORT
9040b41259Schristos FT_XZ,
9140b41259Schristos #endif
9241c9b009Schristos #ifndef NO_LZ_SUPPORT
9341c9b009Schristos FT_LZ,
9441c9b009Schristos #endif
95ecadec7fSmrg FT_LAST,
96ecadec7fSmrg FT_UNKNOWN
97ecadec7fSmrg };
984084ec54Smrg
99b6e59003Stsutsui #ifndef NO_BZIP2_SUPPORT
100ecadec7fSmrg #include <bzlib.h>
101ecadec7fSmrg
102ecadec7fSmrg #define BZ2_SUFFIX ".bz2"
103ecadec7fSmrg #define BZIP2_MAGIC "\102\132\150"
104b6e59003Stsutsui #endif
105b6e59003Stsutsui
106b6e59003Stsutsui #ifndef NO_COMPRESS_SUPPORT
107b6e59003Stsutsui #define Z_SUFFIX ".Z"
108b6e59003Stsutsui #define Z_MAGIC "\037\235"
109b6e59003Stsutsui #endif
110ecadec7fSmrg
111569ceba5Smrg #ifndef NO_PACK_SUPPORT
112569ceba5Smrg #define PACK_MAGIC "\037\036"
113569ceba5Smrg #endif
114569ceba5Smrg
11540b41259Schristos #ifndef NO_XZ_SUPPORT
11640b41259Schristos #include <lzma.h>
11740b41259Schristos #define XZ_SUFFIX ".xz"
11840b41259Schristos #define XZ_MAGIC "\3757zXZ"
11940b41259Schristos #endif
12040b41259Schristos
12141c9b009Schristos #ifndef NO_LZ_SUPPORT
12241c9b009Schristos #define LZ_SUFFIX ".lz"
12341c9b009Schristos #define LZ_MAGIC "LZIP"
12441c9b009Schristos #endif
12541c9b009Schristos
126ecadec7fSmrg #define GZ_SUFFIX ".gz"
127ecadec7fSmrg
1281d0e9794Smrg #define BUFLEN (64 * 1024)
1294084ec54Smrg
13008891561Smrg #define GZIP_MAGIC0 0x1F
131ecadec7fSmrg #define GZIP_MAGIC1 0x8B
132ecadec7fSmrg #define GZIP_OMAGIC1 0x9E
13308891561Smrg
13411153dbcSmrg #define GZIP_TIMESTAMP (off_t)4
1359e9fa784Smrg #define GZIP_ORIGNAME (off_t)10
1369e9fa784Smrg
1371d0e9794Smrg #define HEAD_CRC 0x02
1381d0e9794Smrg #define EXTRA_FIELD 0x04
1394084ec54Smrg #define ORIG_NAME 0x08
1401d0e9794Smrg #define COMMENT 0x10
1414084ec54Smrg
1421d0e9794Smrg #define OS_CODE 3 /* Unix */
1434084ec54Smrg
144d55b8a21Sdsl typedef struct {
145754b92f9Sdsl const char *zipped;
146d55b8a21Sdsl int ziplen;
147754b92f9Sdsl const char *normal; /* for unzip - must not be longer than zipped */
148d55b8a21Sdsl } suffixes_t;
149d55b8a21Sdsl static suffixes_t suffixes[] = {
150754b92f9Sdsl #define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
151d55b8a21Sdsl SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
152d44ac400Smrg #ifndef SMALL
153d55b8a21Sdsl SUFFIX(GZ_SUFFIX, ""),
154d55b8a21Sdsl SUFFIX(".z", ""),
155d55b8a21Sdsl SUFFIX("-gz", ""),
156d55b8a21Sdsl SUFFIX("-z", ""),
157d55b8a21Sdsl SUFFIX("_z", ""),
158d55b8a21Sdsl SUFFIX(".taz", ".tar"),
159d55b8a21Sdsl SUFFIX(".tgz", ".tar"),
160d44ac400Smrg #ifndef NO_BZIP2_SUPPORT
161d55b8a21Sdsl SUFFIX(BZ2_SUFFIX, ""),
162d44ac400Smrg #endif
163d44ac400Smrg #ifndef NO_COMPRESS_SUPPORT
164d55b8a21Sdsl SUFFIX(Z_SUFFIX, ""),
165d44ac400Smrg #endif
1665708f444Schristos #ifndef NO_XZ_SUPPORT
1675708f444Schristos SUFFIX(XZ_SUFFIX, ""),
1685708f444Schristos #endif
16941c9b009Schristos #ifndef NO_LZ_SUPPORT
17041c9b009Schristos SUFFIX(LZ_SUFFIX, ""),
17141c9b009Schristos #endif
172d55b8a21Sdsl SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
173d44ac400Smrg #endif /* SMALL */
174d55b8a21Sdsl #undef SUFFIX
175d55b8a21Sdsl };
176d55b8a21Sdsl #define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
177205ea56fSmrg #define SUFFIX_MAXLEN 30
178d44ac400Smrg
1798f36055fSmrg static const char gzip_version[] = "NetBSD gzip 20240203";
1804084ec54Smrg
1814084ec54Smrg static int cflag; /* stdout mode */
1824084ec54Smrg static int dflag; /* decompress mode */
18378b17b7bSmrg static int lflag; /* list mode */
184a9c239e6Smrg static int numflag = 6; /* gzip -1..-9 value */
185d7f0a538Smrg
18657a27c01Smrg #ifndef SMALL
18757a27c01Smrg static int fflag; /* force mode */
18877a6c12fSmrg static int kflag; /* don't delete input files */
1894084ec54Smrg static int nflag; /* don't save name/timestamp */
1904084ec54Smrg static int Nflag; /* don't restore name/timestamp */
1914084ec54Smrg static int qflag; /* quiet mode */
1924084ec54Smrg static int rflag; /* recursive mode */
193d7f0a538Smrg static int tflag; /* test */
194d7f0a538Smrg static int vflag; /* verbose mode */
195d3697da1Schristos #ifdef SIGINFO
1965e22a92eSmrg static sig_atomic_t print_info = 0;
197d3697da1Schristos #endif
19857a27c01Smrg #else
19957a27c01Smrg #define qflag 0
200754b92f9Sdsl #define tflag 0
20157a27c01Smrg #endif
202d7f0a538Smrg
2032d5e05afSmrg static int exit_value = 0; /* exit value */
2042d5e05afSmrg
2055e22a92eSmrg static const char *infile; /* name of file coming in */
2064084ec54Smrg
2077e57d8feSjoerg static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead;
2087c8d31eeStsutsui #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
2097c8d31eeStsutsui !defined(NO_XZ_SUPPORT)
2107e57d8feSjoerg static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead;
211754b92f9Sdsl #endif
2127e57d8feSjoerg static void maybe_warn(const char *fmt, ...) __printflike(1, 2);
2137e57d8feSjoerg static void maybe_warnx(const char *fmt, ...) __printflike(1, 2);
2147149a529Smrg static enum filetype file_gettype(u_char *);
215d55b8a21Sdsl #ifdef SMALL
216d55b8a21Sdsl #define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
217d55b8a21Sdsl #endif
218d55b8a21Sdsl static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
2192d5e05afSmrg static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
2205fe88849Sagc static off_t file_compress(char *, char *, size_t);
2215fe88849Sagc static off_t file_uncompress(char *, char *, size_t);
2224084ec54Smrg static void handle_pathname(char *);
2234084ec54Smrg static void handle_file(char *, struct stat *);
2244084ec54Smrg static void handle_stdin(void);
2254084ec54Smrg static void handle_stdout(void);
22688899e33Smrg static void print_ratio(off_t, off_t, FILE *);
2279e3ca570Smrg static void print_list(int fd, off_t, const char *, time_t);
2287e57d8feSjoerg __dead static void usage(void);
2297e57d8feSjoerg __dead static void display_version(void);
230d55b8a21Sdsl static const suffixes_t *check_suffix(char *, int);
231751e975bSyamt static ssize_t read_retry(int, void *, size_t);
23213a9a3bbSchristos static ssize_t write_retry(int, const void *, size_t);
2333871bbe7Smartin static void print_list_out(off_t, off_t, const char*);
234d7f0a538Smrg
235d55b8a21Sdsl #ifdef SMALL
2365e22a92eSmrg #define infile_set(f,t) infile_set(f)
2375e22a92eSmrg #endif
2385e22a92eSmrg static void infile_set(const char *newinfile, off_t total);
2395e22a92eSmrg
2405e22a92eSmrg #ifdef SMALL
241d55b8a21Sdsl #define unlink_input(f, sb) unlink(f)
2425e22a92eSmrg #define check_siginfo() /* nothing */
2435e22a92eSmrg #define setup_signals() /* nothing */
2445e22a92eSmrg #define infile_newdata(t) /* nothing */
245d55b8a21Sdsl #else
2465e22a92eSmrg static off_t infile_total; /* total expected to read/write */
2475e22a92eSmrg static off_t infile_current; /* current read/write */
2485e22a92eSmrg
249d3697da1Schristos #ifdef SIGINFO
2505e22a92eSmrg static void check_siginfo(void);
251d3697da1Schristos #else
252d3697da1Schristos #define check_siginfo() /* nothing */
253d3697da1Schristos #endif
254754b92f9Sdsl static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
255d7f0a538Smrg static void prepend_gzip(char *, int *, char ***);
2565c0ab9e0Smrg static void handle_dir(char *);
257d55b8a21Sdsl static void print_verbage(const char *, const char *, off_t, off_t);
2585fe88849Sagc static void print_test(const char *, int);
2591a05f328Smrg static void copymodes(int fd, const struct stat *, const char *file);
2601a05f328Smrg static int check_outfile(const char *outfile);
2615e22a92eSmrg static void setup_signals(void);
2625e22a92eSmrg static void infile_newdata(size_t newdata);
2635e22a92eSmrg static void infile_clear(void);
264d7f0a538Smrg #endif
265d7f0a538Smrg
266b6e59003Stsutsui #ifndef NO_BZIP2_SUPPORT
2671d0e9794Smrg static off_t unbzip2(int, int, char *, size_t, off_t *);
268b6e59003Stsutsui #endif
269d7f0a538Smrg
270b6e59003Stsutsui #ifndef NO_COMPRESS_SUPPORT
271d55b8a21Sdsl static FILE *zdopen(int);
2721d0e9794Smrg static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
273b6e59003Stsutsui #endif
2744084ec54Smrg
275569ceba5Smrg #ifndef NO_PACK_SUPPORT
276569ceba5Smrg static off_t unpack(int, int, char *, size_t, off_t *);
277569ceba5Smrg #endif
278569ceba5Smrg
27940b41259Schristos #ifndef NO_XZ_SUPPORT
28040b41259Schristos static off_t unxz(int, int, char *, size_t, off_t *);
2813871bbe7Smartin static off_t unxz_len(int);
28240b41259Schristos #endif
28340b41259Schristos
28441c9b009Schristos #ifndef NO_LZ_SUPPORT
28541c9b009Schristos static off_t unlz(int, int, char *, size_t, off_t *);
28641c9b009Schristos #endif
28741c9b009Schristos
28809e6110cSmrg #ifdef SMALL
28909e6110cSmrg #define getopt_long(a,b,c,d,e) getopt(a,b,c)
29009e6110cSmrg #else
2913c67b874Sjdolecek static const struct option longopts[] = {
2924084ec54Smrg { "stdout", no_argument, 0, 'c' },
2934084ec54Smrg { "to-stdout", no_argument, 0, 'c' },
2944084ec54Smrg { "decompress", no_argument, 0, 'd' },
2954084ec54Smrg { "uncompress", no_argument, 0, 'd' },
2964084ec54Smrg { "force", no_argument, 0, 'f' },
2974084ec54Smrg { "help", no_argument, 0, 'h' },
29877a6c12fSmrg { "keep", no_argument, 0, 'k' },
29978b17b7bSmrg { "list", no_argument, 0, 'l' },
3004084ec54Smrg { "no-name", no_argument, 0, 'n' },
3014084ec54Smrg { "name", no_argument, 0, 'N' },
3024084ec54Smrg { "quiet", no_argument, 0, 'q' },
3034084ec54Smrg { "recursive", no_argument, 0, 'r' },
3044084ec54Smrg { "suffix", required_argument, 0, 'S' },
3054084ec54Smrg { "test", no_argument, 0, 't' },
3064084ec54Smrg { "verbose", no_argument, 0, 'v' },
3074084ec54Smrg { "version", no_argument, 0, 'V' },
3084084ec54Smrg { "fast", no_argument, 0, '1' },
3094084ec54Smrg { "best", no_argument, 0, '9' },
3104084ec54Smrg #if 0
3114084ec54Smrg /*
312ecadec7fSmrg * This is what else GNU gzip implements. --ascii isn't useful
313ecadec7fSmrg * on NetBSD, and I don't care to have a --license.
3144084ec54Smrg */
3154084ec54Smrg { "ascii", no_argument, 0, 'a' },
3164084ec54Smrg { "license", no_argument, 0, 'L' },
3174084ec54Smrg #endif
318e4b2fd86Smrg { NULL, no_argument, 0, 0 },
3194084ec54Smrg };
32009e6110cSmrg #endif
3214084ec54Smrg
3224084ec54Smrg int
main(int argc,char ** argv)3234084ec54Smrg main(int argc, char **argv)
3244084ec54Smrg {
3254084ec54Smrg const char *progname = getprogname();
32615fba948Smrg #ifndef SMALL
3270b76a77cSmrg char *gzip;
328d55b8a21Sdsl int len;
32957a27c01Smrg #endif
3304084ec54Smrg int ch;
3314084ec54Smrg
3325e22a92eSmrg setup_signals();
3330b76a77cSmrg
33457a27c01Smrg #ifndef SMALL
3350b76a77cSmrg if ((gzip = getenv("GZIP")) != NULL)
3360b76a77cSmrg prepend_gzip(gzip, &argc, &argv);
33715fba948Smrg #endif
3380b76a77cSmrg
3394084ec54Smrg /*
3404084ec54Smrg * XXX
3414084ec54Smrg * handle being called `gunzip', `zcat' and `gzcat'
3424084ec54Smrg */
3434084ec54Smrg if (strcmp(progname, "gunzip") == 0)
3444084ec54Smrg dflag = 1;
3454084ec54Smrg else if (strcmp(progname, "zcat") == 0 ||
3464084ec54Smrg strcmp(progname, "gzcat") == 0)
3474084ec54Smrg dflag = cflag = 1;
34857a27c01Smrg
34957a27c01Smrg #ifdef SMALL
350d8b33778Schristos #define OPT_LIST "123456789cdhlVn"
35157a27c01Smrg #else
35277a6c12fSmrg #define OPT_LIST "123456789cdfhklNnqrS:tVv"
35357a27c01Smrg #endif
3544084ec54Smrg
355d55b8a21Sdsl while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
3564084ec54Smrg switch (ch) {
3572f705835Swiz case '1': case '2': case '3':
3582f705835Swiz case '4': case '5': case '6':
3592f705835Swiz case '7': case '8': case '9':
3602f705835Swiz numflag = ch - '0';
3612f705835Swiz break;
3624084ec54Smrg case 'c':
3634084ec54Smrg cflag = 1;
3644084ec54Smrg break;
3654084ec54Smrg case 'd':
3664084ec54Smrg dflag = 1;
3674084ec54Smrg break;
36878b17b7bSmrg case 'l':
36978b17b7bSmrg lflag = 1;
37078b17b7bSmrg dflag = 1;
37178b17b7bSmrg break;
372d7f0a538Smrg case 'V':
373d7f0a538Smrg display_version();
374d7f0a538Smrg /* NOTREACHED */
37557a27c01Smrg #ifndef SMALL
37657a27c01Smrg case 'f':
37757a27c01Smrg fflag = 1;
37857a27c01Smrg break;
37977a6c12fSmrg case 'k':
38077a6c12fSmrg kflag = 1;
38177a6c12fSmrg break;
3824084ec54Smrg case 'N':
3834084ec54Smrg nflag = 0;
3844084ec54Smrg Nflag = 1;
3854084ec54Smrg break;
3862f705835Swiz case 'n':
3872f705835Swiz nflag = 1;
3882f705835Swiz Nflag = 0;
3892f705835Swiz break;
3904084ec54Smrg case 'q':
3914084ec54Smrg qflag = 1;
3924084ec54Smrg break;
3934084ec54Smrg case 'r':
3944084ec54Smrg rflag = 1;
3954084ec54Smrg break;
3964084ec54Smrg case 'S':
397d55b8a21Sdsl len = strlen(optarg);
398d55b8a21Sdsl if (len != 0) {
399205ea56fSmrg if (len > SUFFIX_MAXLEN)
400205ea56fSmrg errx(1, "incorrect suffix: '%s'", optarg);
401754b92f9Sdsl suffixes[0].zipped = optarg;
402d55b8a21Sdsl suffixes[0].ziplen = len;
403d55b8a21Sdsl } else {
404754b92f9Sdsl suffixes[NUM_SUFFIXES - 1].zipped = "";
405d55b8a21Sdsl suffixes[NUM_SUFFIXES - 1].ziplen = 0;
406d55b8a21Sdsl }
4074084ec54Smrg break;
4084084ec54Smrg case 't':
4094084ec54Smrg cflag = 1;
4104084ec54Smrg tflag = 1;
4114084ec54Smrg dflag = 1;
4124084ec54Smrg break;
4134084ec54Smrg case 'v':
4144084ec54Smrg vflag = 1;
4154084ec54Smrg break;
416d8b33778Schristos #else
417d8b33778Schristos case 'n':
418d8b33778Schristos break;
419d7f0a538Smrg #endif
420e4b2fd86Smrg default:
421e4b2fd86Smrg usage();
422e4b2fd86Smrg /* NOTREACHED */
4234084ec54Smrg }
424d55b8a21Sdsl }
4254084ec54Smrg argv += optind;
4264084ec54Smrg argc -= optind;
4274084ec54Smrg
4284084ec54Smrg if (argc == 0) {
4294084ec54Smrg if (dflag) /* stdin mode */
4304084ec54Smrg handle_stdin();
4314084ec54Smrg else /* stdout mode */
4324084ec54Smrg handle_stdout();
4334084ec54Smrg } else {
4344084ec54Smrg do {
4354084ec54Smrg handle_pathname(argv[0]);
43678b17b7bSmrg } while (*++argv);
4374084ec54Smrg }
438ed20265fSmrg #ifndef SMALL
43988899e33Smrg if (qflag == 0 && lflag && argc > 1)
4409e3ca570Smrg print_list(-1, 0, "(totals)", 0);
441ed20265fSmrg #endif
4422d5e05afSmrg exit(exit_value);
4434084ec54Smrg }
4444084ec54Smrg
4454084ec54Smrg /* maybe print a warning */
4464084ec54Smrg void
maybe_warn(const char * fmt,...)4474084ec54Smrg maybe_warn(const char *fmt, ...)
4484084ec54Smrg {
4494084ec54Smrg va_list ap;
4504084ec54Smrg
4514084ec54Smrg if (qflag == 0) {
4524084ec54Smrg va_start(ap, fmt);
4534084ec54Smrg vwarn(fmt, ap);
4544084ec54Smrg va_end(ap);
4554084ec54Smrg }
4562d5e05afSmrg if (exit_value == 0)
4572d5e05afSmrg exit_value = 1;
4584084ec54Smrg }
4594084ec54Smrg
4602d5e05afSmrg /* ... without an errno. */
4614084ec54Smrg void
maybe_warnx(const char * fmt,...)4624084ec54Smrg maybe_warnx(const char *fmt, ...)
4634084ec54Smrg {
4644084ec54Smrg va_list ap;
4654084ec54Smrg
4664084ec54Smrg if (qflag == 0) {
4674084ec54Smrg va_start(ap, fmt);
4684084ec54Smrg vwarnx(fmt, ap);
4694084ec54Smrg va_end(ap);
4704084ec54Smrg }
4712d5e05afSmrg if (exit_value == 0)
4722d5e05afSmrg exit_value = 1;
4734084ec54Smrg }
4744084ec54Smrg
4752d5e05afSmrg /* maybe print an error */
4764084ec54Smrg void
maybe_err(const char * fmt,...)4772d5e05afSmrg maybe_err(const char *fmt, ...)
4784084ec54Smrg {
4794084ec54Smrg va_list ap;
4804084ec54Smrg
4814084ec54Smrg if (qflag == 0) {
4824084ec54Smrg va_start(ap, fmt);
4834084ec54Smrg vwarn(fmt, ap);
4844084ec54Smrg va_end(ap);
4854084ec54Smrg }
4862d5e05afSmrg exit(2);
4874084ec54Smrg }
4884084ec54Smrg
4897c8d31eeStsutsui #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
4907c8d31eeStsutsui !defined(NO_XZ_SUPPORT)
4912d5e05afSmrg /* ... without an errno. */
49208891561Smrg void
maybe_errx(const char * fmt,...)4932d5e05afSmrg maybe_errx(const char *fmt, ...)
49408891561Smrg {
49508891561Smrg va_list ap;
49608891561Smrg
49708891561Smrg if (qflag == 0) {
49808891561Smrg va_start(ap, fmt);
49908891561Smrg vwarnx(fmt, ap);
50008891561Smrg va_end(ap);
50108891561Smrg }
5022d5e05afSmrg exit(2);
50308891561Smrg }
504754b92f9Sdsl #endif
50508891561Smrg
50657a27c01Smrg #ifndef SMALL
5070b76a77cSmrg /* split up $GZIP and prepend it to the argument list */
5080b76a77cSmrg static void
prepend_gzip(char * gzip,int * argc,char *** argv)5090b76a77cSmrg prepend_gzip(char *gzip, int *argc, char ***argv)
5100b76a77cSmrg {
5110b76a77cSmrg char *s, **nargv, **ac;
5120b76a77cSmrg int nenvarg = 0, i;
5130b76a77cSmrg
5140b76a77cSmrg /* scan how many arguments there are */
5154cc9b2f8Senami for (s = gzip;;) {
5164cc9b2f8Senami while (*s == ' ' || *s == '\t')
5174cc9b2f8Senami s++;
5184cc9b2f8Senami if (*s == 0)
5194cc9b2f8Senami goto count_done;
5200b76a77cSmrg nenvarg++;
5214cc9b2f8Senami while (*s != ' ' && *s != '\t')
5224cc9b2f8Senami if (*s++ == 0)
5234cc9b2f8Senami goto count_done;
5240b76a77cSmrg }
5254cc9b2f8Senami count_done:
5260b76a77cSmrg /* punt early */
5270b76a77cSmrg if (nenvarg == 0)
5280b76a77cSmrg return;
5290b76a77cSmrg
5300b76a77cSmrg *argc += nenvarg;
5310b76a77cSmrg ac = *argv;
5320b76a77cSmrg
53395a042feSsimonb nargv = (char **)malloc((*argc + 1) * sizeof(char *));
5340b76a77cSmrg if (nargv == NULL)
5352d5e05afSmrg maybe_err("malloc");
5360b76a77cSmrg
5370b76a77cSmrg /* stash this away */
5380b76a77cSmrg *argv = nargv;
5390b76a77cSmrg
5400b76a77cSmrg /* copy the program name first */
5410b76a77cSmrg i = 0;
5420b76a77cSmrg nargv[i++] = *(ac++);
5430b76a77cSmrg
5440b76a77cSmrg /* take a copy of $GZIP and add it to the array */
5450b76a77cSmrg s = strdup(gzip);
5460b76a77cSmrg if (s == NULL)
5472d5e05afSmrg maybe_err("strdup");
5484cc9b2f8Senami for (;;) {
5494cc9b2f8Senami /* Skip whitespaces. */
5504cc9b2f8Senami while (*s == ' ' || *s == '\t')
5514cc9b2f8Senami s++;
5524cc9b2f8Senami if (*s == 0)
5534cc9b2f8Senami goto copy_done;
5540b76a77cSmrg nargv[i++] = s;
5554cc9b2f8Senami /* Find the end of this argument. */
5564cc9b2f8Senami while (*s != ' ' && *s != '\t')
5574cc9b2f8Senami if (*s++ == 0)
5584cc9b2f8Senami /* Argument followed by NUL. */
5594cc9b2f8Senami goto copy_done;
5604cc9b2f8Senami /* Terminate by overwriting ' ' or '\t' with NUL. */
5614cc9b2f8Senami *s++ = 0;
5620b76a77cSmrg }
5634cc9b2f8Senami copy_done:
5640b76a77cSmrg
5650b76a77cSmrg /* copy the original arguments and a NULL */
5660b76a77cSmrg while (*ac)
5670b76a77cSmrg nargv[i++] = *(ac++);
5680b76a77cSmrg nargv[i] = NULL;
5690b76a77cSmrg }
57057a27c01Smrg #endif
5710b76a77cSmrg
572d55b8a21Sdsl /* compress input to output. Return bytes read, -1 on error */
5731d0e9794Smrg static off_t
gz_compress(int in,int out,off_t * gsizep,const char * origname,uint32_t mtime)574d55b8a21Sdsl gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
5754084ec54Smrg {
5761d0e9794Smrg z_stream z;
5777ae4fdd2She char *outbufp, *inbufp;
5781d0e9794Smrg off_t in_tot = 0, out_tot = 0;
5791d0e9794Smrg ssize_t in_size;
5801d0e9794Smrg int i, error;
5811d0e9794Smrg uLong crc;
582d55b8a21Sdsl #ifdef SMALL
583d55b8a21Sdsl static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
584d55b8a21Sdsl 0, 0, 0, 0,
585d55b8a21Sdsl 0, OS_CODE };
586d55b8a21Sdsl #endif
5874084ec54Smrg
588d55b8a21Sdsl outbufp = malloc(BUFLEN);
589d55b8a21Sdsl inbufp = malloc(BUFLEN);
590d55b8a21Sdsl if (outbufp == NULL || inbufp == NULL) {
5917ae4fdd2She maybe_err("malloc failed");
5922d5e05afSmrg goto out;
5932d5e05afSmrg }
5941d0e9794Smrg
5951d0e9794Smrg memset(&z, 0, sizeof z);
5964db02197Smrg z.zalloc = Z_NULL;
5974db02197Smrg z.zfree = Z_NULL;
5981d0e9794Smrg z.opaque = 0;
5991d0e9794Smrg
600d55b8a21Sdsl #ifdef SMALL
601d55b8a21Sdsl memcpy(outbufp, header, sizeof header);
602d55b8a21Sdsl i = sizeof header;
603d55b8a21Sdsl #else
604d55b8a21Sdsl if (nflag != 0) {
605d55b8a21Sdsl mtime = 0;
606d55b8a21Sdsl origname = "";
607d55b8a21Sdsl }
608d55b8a21Sdsl
609d55b8a21Sdsl i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
610d55b8a21Sdsl GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
611d55b8a21Sdsl *origname ? ORIG_NAME : 0,
612d55b8a21Sdsl mtime & 0xff,
613d55b8a21Sdsl (mtime >> 8) & 0xff,
614d55b8a21Sdsl (mtime >> 16) & 0xff,
615d55b8a21Sdsl (mtime >> 24) & 0xff,
616434117bcSdsl numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
617434117bcSdsl OS_CODE, origname);
618d55b8a21Sdsl if (i >= BUFLEN)
619d55b8a21Sdsl /* this need PATH_MAX > BUFLEN ... */
620d55b8a21Sdsl maybe_err("snprintf");
621d55b8a21Sdsl if (*origname)
622d55b8a21Sdsl i++;
623d55b8a21Sdsl #endif
624d55b8a21Sdsl
6258cba5925Sjoerg z.next_out = (unsigned char *)outbufp + i;
626d55b8a21Sdsl z.avail_out = BUFLEN - i;
627d55b8a21Sdsl
6281d0e9794Smrg error = deflateInit2(&z, numflag, Z_DEFLATED,
6295c0ab9e0Smrg (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
6302d5e05afSmrg if (error != Z_OK) {
6312d5e05afSmrg maybe_warnx("deflateInit2 failed");
6322d5e05afSmrg in_tot = -1;
6332d5e05afSmrg goto out;
6342d5e05afSmrg }
6351d0e9794Smrg
6361d0e9794Smrg crc = crc32(0L, Z_NULL, 0);
6374084ec54Smrg for (;;) {
6386c26c0e3Ssimonb check_siginfo();
6391d0e9794Smrg if (z.avail_out == 0) {
64013a9a3bbSchristos if (write_retry(out, outbufp, BUFLEN) != BUFLEN) {
6412d5e05afSmrg maybe_warn("write");
642bb915868Smrg out_tot = -1;
6432d5e05afSmrg goto out;
6442d5e05afSmrg }
6451d0e9794Smrg
6467ae4fdd2She out_tot += BUFLEN;
6478cba5925Sjoerg z.next_out = (unsigned char *)outbufp;
6487ae4fdd2She z.avail_out = BUFLEN;
6491d0e9794Smrg }
6501d0e9794Smrg
6511d0e9794Smrg if (z.avail_in == 0) {
652d55b8a21Sdsl in_size = read(in, inbufp, BUFLEN);
653d55b8a21Sdsl if (in_size < 0) {
654d55b8a21Sdsl maybe_warn("read");
6552d5e05afSmrg in_tot = -1;
6562d5e05afSmrg goto out;
6572d5e05afSmrg }
6581d0e9794Smrg if (in_size == 0)
6594084ec54Smrg break;
6605e22a92eSmrg infile_newdata(in_size);
6614084ec54Smrg
6627ae4fdd2She crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
6631d0e9794Smrg in_tot += in_size;
6648cba5925Sjoerg z.next_in = (unsigned char *)inbufp;
6651d0e9794Smrg z.avail_in = in_size;
6664084ec54Smrg }
6671d0e9794Smrg
6681d0e9794Smrg error = deflate(&z, Z_NO_FLUSH);
6692d5e05afSmrg if (error != Z_OK && error != Z_STREAM_END) {
6702d5e05afSmrg maybe_warnx("deflate failed");
6712d5e05afSmrg in_tot = -1;
6722d5e05afSmrg goto out;
6732d5e05afSmrg }
6741d0e9794Smrg }
6751d0e9794Smrg
6761d0e9794Smrg /* clean up */
6771d0e9794Smrg for (;;) {
6781d0e9794Smrg size_t len;
6791a05f328Smrg ssize_t w;
6801d0e9794Smrg
6811d0e9794Smrg error = deflate(&z, Z_FINISH);
6822d5e05afSmrg if (error != Z_OK && error != Z_STREAM_END) {
6832d5e05afSmrg maybe_warnx("deflate failed");
6842d5e05afSmrg in_tot = -1;
6852d5e05afSmrg goto out;
6862d5e05afSmrg }
6871d0e9794Smrg
688d55b8a21Sdsl len = (char *)z.next_out - outbufp;
6891d0e9794Smrg
69013a9a3bbSchristos w = write_retry(out, outbufp, len);
6911a05f328Smrg if (w == -1 || (size_t)w != len) {
6922d5e05afSmrg maybe_warn("write");
6932d5e05afSmrg out_tot = -1;
6942d5e05afSmrg goto out;
6952d5e05afSmrg }
6961d0e9794Smrg out_tot += len;
6978cba5925Sjoerg z.next_out = (unsigned char *)outbufp;
6987ae4fdd2She z.avail_out = BUFLEN;
6991d0e9794Smrg
7001d0e9794Smrg if (error == Z_STREAM_END)
7011d0e9794Smrg break;
7021d0e9794Smrg }
7031d0e9794Smrg
7042d5e05afSmrg if (deflateEnd(&z) != Z_OK) {
7052d5e05afSmrg maybe_warnx("deflateEnd failed");
7062d5e05afSmrg in_tot = -1;
7072d5e05afSmrg goto out;
7082d5e05afSmrg }
7091d0e9794Smrg
710d55b8a21Sdsl i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
7111d0e9794Smrg (int)crc & 0xff,
7121d0e9794Smrg (int)(crc >> 8) & 0xff,
7131d0e9794Smrg (int)(crc >> 16) & 0xff,
7141d0e9794Smrg (int)(crc >> 24) & 0xff,
7151d0e9794Smrg (int)in_tot & 0xff,
7161d0e9794Smrg (int)(in_tot >> 8) & 0xff,
7171d0e9794Smrg (int)(in_tot >> 16) & 0xff,
7181d0e9794Smrg (int)(in_tot >> 24) & 0xff);
7191d0e9794Smrg if (i != 8)
720d55b8a21Sdsl maybe_err("snprintf");
721426bb686Smrg #if 0
722cde2923dSmrg if (in_tot > 0xffffffff)
723cde2923dSmrg maybe_warn("input file size >= 4GB cannot be saved");
724426bb686Smrg #endif
72513a9a3bbSchristos if (write_retry(out, outbufp, i) != i) {
7262d5e05afSmrg maybe_warn("write");
7272d5e05afSmrg in_tot = -1;
728d55b8a21Sdsl } else
729d55b8a21Sdsl out_tot += i;
7301d0e9794Smrg
7312d5e05afSmrg out:
732d55b8a21Sdsl if (inbufp != NULL)
7337ae4fdd2She free(inbufp);
734d55b8a21Sdsl if (outbufp != NULL)
7357ae4fdd2She free(outbufp);
7361d0e9794Smrg if (gsizep)
7371d0e9794Smrg *gsizep = out_tot;
7381d0e9794Smrg return in_tot;
7394084ec54Smrg }
7404084ec54Smrg
7411d0e9794Smrg /*
7421d0e9794Smrg * uncompress input to output then close the input. return the
7431d0e9794Smrg * uncompressed size written, and put the compressed sized read
7441d0e9794Smrg * into `*gsizep'.
7451d0e9794Smrg */
7464084ec54Smrg static off_t
gz_uncompress(int in,int out,char * pre,size_t prelen,off_t * gsizep,const char * filename)7472d5e05afSmrg gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
7482d5e05afSmrg const char *filename)
7494084ec54Smrg {
7501d0e9794Smrg z_stream z;
7517ae4fdd2She char *outbufp, *inbufp;
7526b5252b1Slukem off_t out_tot = -1, in_tot = 0;
7536b5252b1Slukem uint32_t out_sub_tot = 0;
7541d0e9794Smrg enum {
7551d0e9794Smrg GZSTATE_MAGIC0,
7561d0e9794Smrg GZSTATE_MAGIC1,
7571d0e9794Smrg GZSTATE_METHOD,
7581d0e9794Smrg GZSTATE_FLAGS,
7591d0e9794Smrg GZSTATE_SKIPPING,
7601d0e9794Smrg GZSTATE_EXTRA,
7611d0e9794Smrg GZSTATE_EXTRA2,
7621d0e9794Smrg GZSTATE_EXTRA3,
7631d0e9794Smrg GZSTATE_ORIGNAME,
7641d0e9794Smrg GZSTATE_COMMENT,
7651d0e9794Smrg GZSTATE_HEAD_CRC1,
7661d0e9794Smrg GZSTATE_HEAD_CRC2,
7671d0e9794Smrg GZSTATE_INIT,
768e46324f3Smrg GZSTATE_READ,
769e46324f3Smrg GZSTATE_CRC,
770e46324f3Smrg GZSTATE_LEN,
7711d0e9794Smrg } state = GZSTATE_MAGIC0;
7721d0e9794Smrg int flags = 0, skip_count = 0;
7736b5252b1Slukem int error = Z_STREAM_ERROR, done_reading = 0;
7746b5252b1Slukem uLong crc = 0;
7752c25900bSdsl ssize_t wr;
776751e975bSyamt int needmore = 0;
7774084ec54Smrg
7781d0e9794Smrg #define ADVANCE() { z.next_in++; z.avail_in--; }
7794084ec54Smrg
7807ae4fdd2She if ((outbufp = malloc(BUFLEN)) == NULL) {
7817ae4fdd2She maybe_err("malloc failed");
7827ae4fdd2She goto out2;
7837ae4fdd2She }
7847ae4fdd2She if ((inbufp = malloc(BUFLEN)) == NULL) {
7857ae4fdd2She maybe_err("malloc failed");
7867ae4fdd2She goto out1;
7877ae4fdd2She }
7887ae4fdd2She
7891d0e9794Smrg memset(&z, 0, sizeof z);
7901d0e9794Smrg z.avail_in = prelen;
7918cba5925Sjoerg z.next_in = (unsigned char *)pre;
7927ae4fdd2She z.avail_out = BUFLEN;
7938cba5925Sjoerg z.next_out = (unsigned char *)outbufp;
7941d0e9794Smrg z.zalloc = NULL;
7951d0e9794Smrg z.zfree = NULL;
7961d0e9794Smrg z.opaque = 0;
7971d0e9794Smrg
7981d0e9794Smrg in_tot = prelen;
7991d0e9794Smrg out_tot = 0;
8001d0e9794Smrg
8011d0e9794Smrg for (;;) {
8025e22a92eSmrg check_siginfo();
803751e975bSyamt if ((z.avail_in == 0 || needmore) && done_reading == 0) {
8041a05f328Smrg ssize_t in_size;
805751e975bSyamt
806751e975bSyamt if (z.avail_in > 0) {
807751e975bSyamt memmove(inbufp, z.next_in, z.avail_in);
808751e975bSyamt }
8098cba5925Sjoerg z.next_in = (unsigned char *)inbufp;
810751e975bSyamt in_size = read(in, z.next_in + z.avail_in,
811751e975bSyamt BUFLEN - z.avail_in);
8121d0e9794Smrg
8131d0e9794Smrg if (in_size == -1) {
81413e5b34eSmrg maybe_warn("failed to read stdin");
815cde2923dSmrg goto stop_and_fail;
816751e975bSyamt } else if (in_size == 0) {
8171d0e9794Smrg done_reading = 1;
818751e975bSyamt }
8195e22a92eSmrg infile_newdata(in_size);
8201d0e9794Smrg
821751e975bSyamt z.avail_in += in_size;
822751e975bSyamt needmore = 0;
8231d0e9794Smrg
8241d0e9794Smrg in_tot += in_size;
8251d0e9794Smrg }
826469c821eSmrg if (z.avail_in == 0) {
827a3ee681fSmlelstv if (done_reading && state != GZSTATE_MAGIC0) {
828e46324f3Smrg maybe_warnx("%s: unexpected end of file",
829e46324f3Smrg filename);
830a3ee681fSmlelstv goto stop_and_fail;
831a3ee681fSmlelstv }
832469c821eSmrg goto stop;
833469c821eSmrg }
8341d0e9794Smrg switch (state) {
8351d0e9794Smrg case GZSTATE_MAGIC0:
8362d5e05afSmrg if (*z.next_in != GZIP_MAGIC0) {
8370ce7ae1eSmlelstv if (in_tot > 0) {
838712a3f3aSmrg maybe_warnx("%s: trailing garbage "
839712a3f3aSmrg "ignored", filename);
8400ce7ae1eSmlelstv goto stop;
8410ce7ae1eSmlelstv }
842e46324f3Smrg maybe_warnx("input not gziped (MAGIC0)");
84360c6cf91Smrg exit_value = 2;
844cde2923dSmrg goto stop_and_fail;
8452d5e05afSmrg }
8461d0e9794Smrg ADVANCE();
8471d0e9794Smrg state++;
848469c821eSmrg out_sub_tot = 0;
849469c821eSmrg crc = crc32(0L, Z_NULL, 0);
8501d0e9794Smrg break;
8511d0e9794Smrg
8521d0e9794Smrg case GZSTATE_MAGIC1:
8531d0e9794Smrg if (*z.next_in != GZIP_MAGIC1 &&
8542d5e05afSmrg *z.next_in != GZIP_OMAGIC1) {
855e46324f3Smrg maybe_warnx("input not gziped (MAGIC1)");
856cde2923dSmrg goto stop_and_fail;
8572d5e05afSmrg }
8581d0e9794Smrg ADVANCE();
8591d0e9794Smrg state++;
8601d0e9794Smrg break;
8611d0e9794Smrg
8621d0e9794Smrg case GZSTATE_METHOD:
8632d5e05afSmrg if (*z.next_in != Z_DEFLATED) {
86413e5b34eSmrg maybe_warnx("unknown compression method");
865cde2923dSmrg goto stop_and_fail;
8662d5e05afSmrg }
8671d0e9794Smrg ADVANCE();
8681d0e9794Smrg state++;
8691d0e9794Smrg break;
8701d0e9794Smrg
8711d0e9794Smrg case GZSTATE_FLAGS:
8721d0e9794Smrg flags = *z.next_in;
8731d0e9794Smrg ADVANCE();
8741d0e9794Smrg skip_count = 6;
8751d0e9794Smrg state++;
8761d0e9794Smrg break;
8771d0e9794Smrg
8781d0e9794Smrg case GZSTATE_SKIPPING:
8791d0e9794Smrg if (skip_count > 0) {
8801d0e9794Smrg skip_count--;
8811d0e9794Smrg ADVANCE();
8824084ec54Smrg } else
8831d0e9794Smrg state++;
8841d0e9794Smrg break;
8851d0e9794Smrg
8861d0e9794Smrg case GZSTATE_EXTRA:
8871d0e9794Smrg if ((flags & EXTRA_FIELD) == 0) {
8881d0e9794Smrg state = GZSTATE_ORIGNAME;
8894084ec54Smrg break;
8904084ec54Smrg }
8911d0e9794Smrg skip_count = *z.next_in;
8921d0e9794Smrg ADVANCE();
8931d0e9794Smrg state++;
8941d0e9794Smrg break;
8954084ec54Smrg
8961d0e9794Smrg case GZSTATE_EXTRA2:
8971d0e9794Smrg skip_count |= ((*z.next_in) << 8);
8981d0e9794Smrg ADVANCE();
8991d0e9794Smrg state++;
9001d0e9794Smrg break;
9014084ec54Smrg
9021d0e9794Smrg case GZSTATE_EXTRA3:
9031d0e9794Smrg if (skip_count > 0) {
9041d0e9794Smrg skip_count--;
9051d0e9794Smrg ADVANCE();
9061d0e9794Smrg } else
9071d0e9794Smrg state++;
9081d0e9794Smrg break;
9091d0e9794Smrg
9101d0e9794Smrg case GZSTATE_ORIGNAME:
9111d0e9794Smrg if ((flags & ORIG_NAME) == 0) {
9121d0e9794Smrg state++;
9131d0e9794Smrg break;
9141d0e9794Smrg }
9151d0e9794Smrg if (*z.next_in == 0)
9161d0e9794Smrg state++;
9171d0e9794Smrg ADVANCE();
9181d0e9794Smrg break;
9191d0e9794Smrg
9201d0e9794Smrg case GZSTATE_COMMENT:
9211d0e9794Smrg if ((flags & COMMENT) == 0) {
9221d0e9794Smrg state++;
9231d0e9794Smrg break;
9241d0e9794Smrg }
9251d0e9794Smrg if (*z.next_in == 0)
9261d0e9794Smrg state++;
9271d0e9794Smrg ADVANCE();
9281d0e9794Smrg break;
9291d0e9794Smrg
9301d0e9794Smrg case GZSTATE_HEAD_CRC1:
9311d0e9794Smrg if (flags & HEAD_CRC)
9321d0e9794Smrg skip_count = 2;
9331d0e9794Smrg else
9341d0e9794Smrg skip_count = 0;
9351d0e9794Smrg state++;
9361d0e9794Smrg break;
9371d0e9794Smrg
9381d0e9794Smrg case GZSTATE_HEAD_CRC2:
9391d0e9794Smrg if (skip_count > 0) {
9401d0e9794Smrg skip_count--;
9411d0e9794Smrg ADVANCE();
9421d0e9794Smrg } else
9431d0e9794Smrg state++;
9441d0e9794Smrg break;
9451d0e9794Smrg
9461d0e9794Smrg case GZSTATE_INIT:
9471d0e9794Smrg if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
94813e5b34eSmrg maybe_warnx("failed to inflateInit");
949cde2923dSmrg goto stop_and_fail;
9501d0e9794Smrg }
9511d0e9794Smrg state++;
9521d0e9794Smrg break;
9531d0e9794Smrg
9541d0e9794Smrg case GZSTATE_READ:
9551d0e9794Smrg error = inflate(&z, Z_FINISH);
956cde2923dSmrg switch (error) {
9573c372acdSmrg /* Z_BUF_ERROR goes with Z_FINISH... */
958cde2923dSmrg case Z_BUF_ERROR:
95977a6c12fSmrg if (z.avail_out > 0 && !done_reading)
96077a6c12fSmrg continue;
96177a6c12fSmrg
962cde2923dSmrg case Z_STREAM_END:
963cde2923dSmrg case Z_OK:
9642c25900bSdsl break;
965cde2923dSmrg
966cde2923dSmrg case Z_NEED_DICT:
967cde2923dSmrg maybe_warnx("Z_NEED_DICT error");
968cde2923dSmrg goto stop_and_fail;
969cde2923dSmrg case Z_DATA_ERROR:
970cde2923dSmrg maybe_warnx("data stream error");
971cde2923dSmrg goto stop_and_fail;
972cde2923dSmrg case Z_STREAM_ERROR:
973cde2923dSmrg maybe_warnx("internal stream error");
974cde2923dSmrg goto stop_and_fail;
975cde2923dSmrg case Z_MEM_ERROR:
976cde2923dSmrg maybe_warnx("memory allocation error");
977cde2923dSmrg goto stop_and_fail;
978cde2923dSmrg
979cde2923dSmrg default:
980cde2923dSmrg maybe_warn("unknown error from inflate(): %d",
981cde2923dSmrg error);
982cde2923dSmrg }
9832c25900bSdsl wr = BUFLEN - z.avail_out;
9841d0e9794Smrg
9852c25900bSdsl if (wr != 0) {
9867ae4fdd2She crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
9871d0e9794Smrg if (
988d7f0a538Smrg #ifndef SMALL
9894084ec54Smrg /* don't write anything with -t */
9901d0e9794Smrg tflag == 0 &&
9911d0e9794Smrg #endif
99213a9a3bbSchristos write_retry(out, outbufp, wr) != wr) {
993b6ec63a3Syamt maybe_warn("error writing to output");
994cde2923dSmrg goto stop_and_fail;
9952d5e05afSmrg }
996d6dd48bcSmrg
997d6dd48bcSmrg out_tot += wr;
998469c821eSmrg out_sub_tot += wr;
9992c25900bSdsl }
10001d0e9794Smrg
10017ed5da7bSmrg if (error == Z_STREAM_END) {
10027ed5da7bSmrg inflateEnd(&z);
1003e46324f3Smrg state++;
10047ed5da7bSmrg }
1005d6dd48bcSmrg
10068cba5925Sjoerg z.next_out = (unsigned char *)outbufp;
10071d0e9794Smrg z.avail_out = BUFLEN;
10081d0e9794Smrg
10091d0e9794Smrg break;
1010e46324f3Smrg case GZSTATE_CRC:
1011e46324f3Smrg {
1012e46324f3Smrg uLong origcrc;
1013e46324f3Smrg
1014e46324f3Smrg if (z.avail_in < 4) {
1015751e975bSyamt if (!done_reading) {
1016751e975bSyamt needmore = 1;
1017e46324f3Smrg continue;
1018751e975bSyamt }
1019e46324f3Smrg maybe_warnx("truncated input");
1020cde2923dSmrg goto stop_and_fail;
1021e46324f3Smrg }
1022668894b1Smrg origcrc = ((unsigned)z.next_in[0] & 0xff) |
1023668894b1Smrg ((unsigned)z.next_in[1] & 0xff) << 8 |
1024668894b1Smrg ((unsigned)z.next_in[2] & 0xff) << 16 |
1025668894b1Smrg ((unsigned)z.next_in[3] & 0xff) << 24;
1026668894b1Smrg if (origcrc != crc) {
1027e46324f3Smrg maybe_warnx("invalid compressed"
1028e46324f3Smrg " data--crc error");
1029cde2923dSmrg goto stop_and_fail;
1030668894b1Smrg }
1031e46324f3Smrg }
1032e46324f3Smrg
1033e46324f3Smrg z.avail_in -= 4;
1034e46324f3Smrg z.next_in += 4;
1035e46324f3Smrg
1036751e975bSyamt if (!z.avail_in && done_reading) {
1037e46324f3Smrg goto stop;
1038751e975bSyamt }
1039e46324f3Smrg state++;
1040e46324f3Smrg break;
1041e46324f3Smrg case GZSTATE_LEN:
1042e46324f3Smrg {
1043e46324f3Smrg uLong origlen;
1044e46324f3Smrg
1045e46324f3Smrg if (z.avail_in < 4) {
1046751e975bSyamt if (!done_reading) {
1047751e975bSyamt needmore = 1;
1048e46324f3Smrg continue;
1049751e975bSyamt }
1050e46324f3Smrg maybe_warnx("truncated input");
1051cde2923dSmrg goto stop_and_fail;
1052e46324f3Smrg }
1053668894b1Smrg origlen = ((unsigned)z.next_in[0] & 0xff) |
1054668894b1Smrg ((unsigned)z.next_in[1] & 0xff) << 8 |
1055668894b1Smrg ((unsigned)z.next_in[2] & 0xff) << 16 |
1056668894b1Smrg ((unsigned)z.next_in[3] & 0xff) << 24;
1057e46324f3Smrg
1058668894b1Smrg if (origlen != out_sub_tot) {
1059e46324f3Smrg maybe_warnx("invalid compressed"
1060e46324f3Smrg " data--length error");
1061cde2923dSmrg goto stop_and_fail;
1062668894b1Smrg }
1063e46324f3Smrg }
1064e46324f3Smrg
1065e46324f3Smrg z.avail_in -= 4;
1066e46324f3Smrg z.next_in += 4;
1067e46324f3Smrg
10681d0e9794Smrg if (error < 0) {
106913e5b34eSmrg maybe_warnx("decompression error");
1070cde2923dSmrg goto stop_and_fail;
10711d0e9794Smrg }
1072e46324f3Smrg state = GZSTATE_MAGIC0;
10731d0e9794Smrg break;
10741d0e9794Smrg }
10754084ec54Smrg continue;
1076cde2923dSmrg stop_and_fail:
1077e3969e9fSmrg out_tot = -1;
10781d0e9794Smrg stop:
10791d0e9794Smrg break;
10801d0e9794Smrg }
10811d0e9794Smrg if (state > GZSTATE_INIT)
10821d0e9794Smrg inflateEnd(&z);
10831d0e9794Smrg
10847ae4fdd2She free(inbufp);
10857ae4fdd2She out1:
10867ae4fdd2She free(outbufp);
10877ae4fdd2She out2:
10881d0e9794Smrg if (gsizep)
10891d0e9794Smrg *gsizep = in_tot;
10901d0e9794Smrg return (out_tot);
10914084ec54Smrg }
10924084ec54Smrg
1093ed20265fSmrg #ifndef SMALL
10944084ec54Smrg /*
10951a05f328Smrg * set the owner, mode, flags & utimes using the given file descriptor.
10961a05f328Smrg * file is only used in possible warning messages.
10974084ec54Smrg */
10984084ec54Smrg static void
copymodes(int fd,const struct stat * sbp,const char * file)10991a05f328Smrg copymodes(int fd, const struct stat *sbp, const char *file)
11004084ec54Smrg {
11014084ec54Smrg struct timeval times[2];
11021a05f328Smrg struct stat sb;
11034084ec54Smrg
11044084ec54Smrg /*
11054084ec54Smrg * If we have no info on the input, give this file some
11064084ec54Smrg * default values and return..
11074084ec54Smrg */
11084084ec54Smrg if (sbp == NULL) {
11094084ec54Smrg mode_t mask = umask(022);
11104084ec54Smrg
11111a05f328Smrg (void)fchmod(fd, DEFFILEMODE & ~mask);
11124084ec54Smrg (void)umask(mask);
11134084ec54Smrg return;
11144084ec54Smrg }
11151a05f328Smrg sb = *sbp;
11164084ec54Smrg
11174084ec54Smrg /* if the chown fails, remove set-id bits as-per compress(1) */
11181a05f328Smrg if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
11194084ec54Smrg if (errno != EPERM)
11201a05f328Smrg maybe_warn("couldn't fchown: %s", file);
11211a05f328Smrg sb.st_mode &= ~(S_ISUID|S_ISGID);
11224084ec54Smrg }
11234084ec54Smrg
11244084ec54Smrg /* we only allow set-id and the 9 normal permission bits */
11251a05f328Smrg sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
11261a05f328Smrg if (fchmod(fd, sb.st_mode) < 0)
11271a05f328Smrg maybe_warn("couldn't fchmod: %s", file);
11284084ec54Smrg
1129*74b93bffSmartin #if !HAVE_NBTOOL_CONFIG_H
11301a05f328Smrg TIMESPEC_TO_TIMEVAL(×[0], &sb.st_atimespec);
11311a05f328Smrg TIMESPEC_TO_TIMEVAL(×[1], &sb.st_mtimespec);
11321a05f328Smrg if (futimes(fd, times) < 0)
11334084ec54Smrg maybe_warn("couldn't utimes: %s", file);
1134ef77be63Smrg
1135ef77be63Smrg /* finally, only try flags if they exist already */
1136ef77be63Smrg if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1137ef77be63Smrg maybe_warn("couldn't fchflags: %s", file);
1138d8b33778Schristos #endif
11394084ec54Smrg }
1140ed20265fSmrg #endif
11414084ec54Smrg
11427149a529Smrg /* what sort of file is this? */
11437149a529Smrg static enum filetype
file_gettype(u_char * buf)11447149a529Smrg file_gettype(u_char *buf)
11457149a529Smrg {
11467149a529Smrg
11477149a529Smrg if (buf[0] == GZIP_MAGIC0 &&
11487149a529Smrg (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
11497149a529Smrg return FT_GZIP;
11507149a529Smrg else
11517149a529Smrg #ifndef NO_BZIP2_SUPPORT
11527149a529Smrg if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
11537149a529Smrg buf[3] >= '0' && buf[3] <= '9')
11547149a529Smrg return FT_BZIP2;
11557149a529Smrg else
11567149a529Smrg #endif
11577149a529Smrg #ifndef NO_COMPRESS_SUPPORT
11587149a529Smrg if (memcmp(buf, Z_MAGIC, 2) == 0)
11597149a529Smrg return FT_Z;
11607149a529Smrg else
11617149a529Smrg #endif
1162569ceba5Smrg #ifndef NO_PACK_SUPPORT
1163569ceba5Smrg if (memcmp(buf, PACK_MAGIC, 2) == 0)
1164569ceba5Smrg return FT_PACK;
1165569ceba5Smrg else
1166569ceba5Smrg #endif
116740b41259Schristos #ifndef NO_XZ_SUPPORT
116840b41259Schristos if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */
116940b41259Schristos return FT_XZ;
117040b41259Schristos else
117140b41259Schristos #endif
117241c9b009Schristos #ifndef NO_LZ_SUPPORT
117341c9b009Schristos if (memcmp(buf, LZ_MAGIC, 4) == 0)
117441c9b009Schristos return FT_LZ;
117541c9b009Schristos else
1176ef95ae4aSskrll #endif
11777149a529Smrg return FT_UNKNOWN;
11787149a529Smrg }
11797149a529Smrg
11802d5e05afSmrg #ifndef SMALL
11812d5e05afSmrg /* check the outfile is OK. */
11822d5e05afSmrg static int
check_outfile(const char * outfile)11831a05f328Smrg check_outfile(const char *outfile)
11842d5e05afSmrg {
11851a05f328Smrg struct stat sb;
11862d5e05afSmrg int ok = 1;
11872d5e05afSmrg
11881a05f328Smrg if (lflag == 0 && stat(outfile, &sb) == 0) {
11892644272fSmrg if (fflag)
11902644272fSmrg unlink(outfile);
11912644272fSmrg else if (isatty(STDIN_FILENO)) {
11922d5e05afSmrg char ans[10] = { 'n', '\0' }; /* default */
11932d5e05afSmrg
11942d5e05afSmrg fprintf(stderr, "%s already exists -- do you wish to "
11952d5e05afSmrg "overwrite (y or n)? " , outfile);
11962d5e05afSmrg (void)fgets(ans, sizeof(ans) - 1, stdin);
11972d5e05afSmrg if (ans[0] != 'y' && ans[0] != 'Y') {
11986bcac52dSmartin fprintf(stderr, "\tnot overwriting\n");
11992d5e05afSmrg ok = 0;
12002d5e05afSmrg } else
12012d5e05afSmrg unlink(outfile);
12022d5e05afSmrg } else {
12032d5e05afSmrg maybe_warnx("%s already exists -- skipping", outfile);
12042d5e05afSmrg ok = 0;
12052d5e05afSmrg }
12062d5e05afSmrg }
12072d5e05afSmrg return ok;
12082d5e05afSmrg }
1209d44ac400Smrg
1210d55b8a21Sdsl static void
unlink_input(const char * file,const struct stat * sb)12111a05f328Smrg unlink_input(const char *file, const struct stat *sb)
1212d44ac400Smrg {
1213d55b8a21Sdsl struct stat nsb;
1214d44ac400Smrg
121577a6c12fSmrg if (kflag)
121677a6c12fSmrg return;
1217d55b8a21Sdsl if (stat(file, &nsb) != 0)
121877a6c12fSmrg /* Must be gone already */
1219d55b8a21Sdsl return;
1220d55b8a21Sdsl if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1221d55b8a21Sdsl /* Definitely a different file */
1222d55b8a21Sdsl return;
1223d55b8a21Sdsl unlink(file);
1224d55b8a21Sdsl }
12255e22a92eSmrg
1226d3697da1Schristos #ifdef SIGINFO
12275e22a92eSmrg static void
got_siginfo(int signo)12285e22a92eSmrg got_siginfo(int signo)
12295e22a92eSmrg {
12305e22a92eSmrg
12315e22a92eSmrg print_info = 1;
12325e22a92eSmrg }
1233d3697da1Schristos #endif
12345e22a92eSmrg
12355e22a92eSmrg static void
setup_signals(void)12365e22a92eSmrg setup_signals(void)
12375e22a92eSmrg {
12385e22a92eSmrg
1239d3697da1Schristos #ifdef SIGINFO
12405e22a92eSmrg signal(SIGINFO, got_siginfo);
1241d3697da1Schristos #endif
12425e22a92eSmrg }
12435e22a92eSmrg
12445e22a92eSmrg static void
infile_newdata(size_t newdata)12455e22a92eSmrg infile_newdata(size_t newdata)
12465e22a92eSmrg {
12475e22a92eSmrg
12485e22a92eSmrg infile_current += newdata;
12495e22a92eSmrg }
1250d55b8a21Sdsl #endif
1251d55b8a21Sdsl
12525e22a92eSmrg static void
infile_set(const char * newinfile,off_t total)12535e22a92eSmrg infile_set(const char *newinfile, off_t total)
12545e22a92eSmrg {
12555e22a92eSmrg
12565e22a92eSmrg if (newinfile)
12575e22a92eSmrg infile = newinfile;
12585e22a92eSmrg #ifndef SMALL
12595e22a92eSmrg infile_total = total;
12605e22a92eSmrg #endif
12615e22a92eSmrg }
12625e22a92eSmrg
12635e22a92eSmrg static void
infile_clear(void)12645e22a92eSmrg infile_clear(void)
12655e22a92eSmrg {
12665e22a92eSmrg
12675e22a92eSmrg infile = NULL;
12685e22a92eSmrg #ifndef SMALL
12695e22a92eSmrg infile_total = infile_current = 0;
12705e22a92eSmrg #endif
12715e22a92eSmrg }
12725e22a92eSmrg
1273d55b8a21Sdsl static const suffixes_t *
check_suffix(char * file,int xlate)1274d55b8a21Sdsl check_suffix(char *file, int xlate)
1275d55b8a21Sdsl {
1276d55b8a21Sdsl const suffixes_t *s;
1277d55b8a21Sdsl int len = strlen(file);
1278d55b8a21Sdsl char *sp;
1279d55b8a21Sdsl
1280d55b8a21Sdsl for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1281668894b1Smrg /* if it doesn't fit in "a.suf", don't bother */
1282d55b8a21Sdsl if (s->ziplen >= len)
1283668894b1Smrg continue;
1284d55b8a21Sdsl sp = file + len - s->ziplen;
1285d55b8a21Sdsl if (strcmp(s->zipped, sp) != 0)
1286d55b8a21Sdsl continue;
1287d55b8a21Sdsl if (xlate)
1288d55b8a21Sdsl strcpy(sp, s->normal);
1289d55b8a21Sdsl return s;
1290d44ac400Smrg }
1291d44ac400Smrg return NULL;
1292d44ac400Smrg }
12932d5e05afSmrg
12944084ec54Smrg /*
12954084ec54Smrg * compress the given file: create a corresponding .gz file and remove the
12964084ec54Smrg * original.
12974084ec54Smrg */
1298d4e27c8dSmrg static off_t
file_compress(char * file,char * outfile,size_t outsize)12995fe88849Sagc file_compress(char *file, char *outfile, size_t outsize)
13004084ec54Smrg {
1301d55b8a21Sdsl int in;
13021d0e9794Smrg int out;
13035e22a92eSmrg off_t size, in_size;
130457a27c01Smrg #ifndef SMALL
1305d55b8a21Sdsl struct stat isb, osb;
1306d55b8a21Sdsl const suffixes_t *suff;
130757a27c01Smrg #endif
13084084ec54Smrg
1309d55b8a21Sdsl in = open(file, O_RDONLY);
1310d55b8a21Sdsl if (in == -1) {
1311d55b8a21Sdsl maybe_warn("can't open %s", file);
1312d55b8a21Sdsl return -1;
1313d55b8a21Sdsl }
1314d55b8a21Sdsl
13155e22a92eSmrg #ifndef SMALL
13165e22a92eSmrg if (fstat(in, &isb) != 0) {
13175e22a92eSmrg close(in);
13185e22a92eSmrg maybe_warn("can't stat %s", file);
13195e22a92eSmrg return -1;
13205e22a92eSmrg }
13215e22a92eSmrg infile_set(file, isb.st_size);
13225e22a92eSmrg #endif
13235e22a92eSmrg
1324bdc79674Smrg if (cflag == 0) {
1325bdc79674Smrg #ifndef SMALL
1326bdc79674Smrg if (isb.st_nlink > 1 && fflag == 0) {
13271a05f328Smrg maybe_warnx("%s has %d other link%s -- "
13281a05f328Smrg "skipping", file, isb.st_nlink - 1,
13291a05f328Smrg isb.st_nlink == 1 ? "" : "s");
1330bdc79674Smrg close(in);
1331bdc79674Smrg return -1;
13322d5e05afSmrg }
13334084ec54Smrg
1334d55b8a21Sdsl if (fflag == 0 && (suff = check_suffix(file, 0))
1335d55b8a21Sdsl && suff->zipped[0] != 0) {
1336d55b8a21Sdsl maybe_warnx("%s already has %s suffix -- unchanged",
1337d55b8a21Sdsl file, suff->zipped);
1338d55b8a21Sdsl close(in);
1339d55b8a21Sdsl return -1;
1340d55b8a21Sdsl }
13414084ec54Smrg #endif
1342d55b8a21Sdsl
1343d55b8a21Sdsl /* Add (usually) .gz to filename */
13441a05f328Smrg if ((size_t)snprintf(outfile, outsize, "%s%s",
1345d55b8a21Sdsl file, suffixes[0].zipped) >= outsize)
1346205ea56fSmrg memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1347d55b8a21Sdsl suffixes[0].zipped, suffixes[0].ziplen + 1);
1348d55b8a21Sdsl
1349d55b8a21Sdsl #ifndef SMALL
13501a05f328Smrg if (check_outfile(outfile) == 0) {
1351d55b8a21Sdsl close(in);
1352d55b8a21Sdsl return -1;
1353d55b8a21Sdsl }
1354d55b8a21Sdsl #endif
1355d55b8a21Sdsl }
1356d55b8a21Sdsl
135741bff69bSbouyer if (cflag == 0) {
13581d0e9794Smrg out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1359d6dd48bcSmrg if (out == -1) {
1360d6dd48bcSmrg maybe_warn("could not create output: %s", outfile);
1361d55b8a21Sdsl fclose(stdin);
1362d55b8a21Sdsl return -1;
1363d6dd48bcSmrg }
13644084ec54Smrg } else
13651d0e9794Smrg out = STDOUT_FILENO;
13664084ec54Smrg
13675e22a92eSmrg in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
13684084ec54Smrg
1369d55b8a21Sdsl (void)close(in);
137099d2aa3dSmrg
13714084ec54Smrg /*
13725e22a92eSmrg * If there was an error, in_size will be -1.
1373d55b8a21Sdsl * If we compressed to stdout, just return the size.
1374d55b8a21Sdsl * Otherwise stat the file and check it is the correct size.
1375d55b8a21Sdsl * We only blow away the file if we can stat the output and it
1376d55b8a21Sdsl * has the expected size.
13774084ec54Smrg */
1378d55b8a21Sdsl if (cflag != 0)
13795e22a92eSmrg return in_size == -1 ? -1 : size;
1380d55b8a21Sdsl
1381d55b8a21Sdsl #ifndef SMALL
13821a05f328Smrg if (fstat(out, &osb) != 0) {
13834084ec54Smrg maybe_warn("couldn't stat: %s", outfile);
1384d55b8a21Sdsl goto bad_outfile;
13854084ec54Smrg }
13864084ec54Smrg
1387d55b8a21Sdsl if (osb.st_size != size) {
1388d55b8a21Sdsl maybe_warnx("output file: %s wrong size (%" PRIdOFF
1389d55b8a21Sdsl " != %" PRIdOFF "), deleting",
1390d55b8a21Sdsl outfile, osb.st_size, size);
1391d55b8a21Sdsl goto bad_outfile;
1392d55b8a21Sdsl }
1393d55b8a21Sdsl
13941a05f328Smrg copymodes(out, &isb, outfile);
1395d55b8a21Sdsl #endif
13961a05f328Smrg if (close(out) == -1)
13971a05f328Smrg maybe_warn("couldn't close output");
1398d55b8a21Sdsl
1399d55b8a21Sdsl /* output is good, ok to delete input */
1400d55b8a21Sdsl unlink_input(file, &isb);
1401d55b8a21Sdsl return size;
1402d55b8a21Sdsl
1403d55b8a21Sdsl #ifndef SMALL
1404d55b8a21Sdsl bad_outfile:
14051a05f328Smrg if (close(out) == -1)
14061a05f328Smrg maybe_warn("couldn't close output");
14071a05f328Smrg
1408d55b8a21Sdsl maybe_warnx("leaving original %s", file);
1409d55b8a21Sdsl unlink(outfile);
1410d55b8a21Sdsl return size;
1411d55b8a21Sdsl #endif
14124084ec54Smrg }
14134084ec54Smrg
14144084ec54Smrg /* uncompress the given file and remove the original */
1415d4e27c8dSmrg static off_t
file_uncompress(char * file,char * outfile,size_t outsize)14165fe88849Sagc file_uncompress(char *file, char *outfile, size_t outsize)
14174084ec54Smrg {
14184084ec54Smrg struct stat isb, osb;
14194084ec54Smrg off_t size;
1420d55b8a21Sdsl ssize_t rbytes;
14219e6cc36bSsimonb unsigned char fourbytes[4];
1422ecadec7fSmrg enum filetype method;
142338af5418Sskrll int fd, ofd, zfd = -1;
14245e22a92eSmrg size_t in_size;
142511153dbcSmrg #ifndef SMALL
1426f143270cSlukem ssize_t rv;
142711153dbcSmrg time_t timestamp = 0;
1428a1b1261aSmrg char name[PATH_MAX + 1];
142911153dbcSmrg #endif
14304084ec54Smrg
14314084ec54Smrg /* gather the old name info */
14324084ec54Smrg
14334084ec54Smrg fd = open(file, O_RDONLY);
14342d5e05afSmrg if (fd < 0) {
14352d5e05afSmrg maybe_warn("can't open %s", file);
14362d5e05afSmrg goto lose;
14372d5e05afSmrg }
14385e22a92eSmrg if (fstat(fd, &isb) != 0) {
14395e22a92eSmrg close(fd);
14405e22a92eSmrg maybe_warn("can't stat %s", file);
14415e22a92eSmrg goto lose;
14425e22a92eSmrg }
14435e22a92eSmrg if (S_ISREG(isb.st_mode))
14445e22a92eSmrg in_size = isb.st_size;
14455e22a92eSmrg else
14465e22a92eSmrg in_size = 0;
14475e22a92eSmrg infile_set(file, in_size);
1448d55b8a21Sdsl
1449d55b8a21Sdsl strlcpy(outfile, file, outsize);
1450d55b8a21Sdsl if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1451d55b8a21Sdsl maybe_warnx("%s: unknown suffix -- ignored", file);
1452d55b8a21Sdsl goto lose;
1453d55b8a21Sdsl }
1454d55b8a21Sdsl
14559e6cc36bSsimonb rbytes = read(fd, fourbytes, sizeof fourbytes);
14569e6cc36bSsimonb if (rbytes != sizeof fourbytes) {
1457ecadec7fSmrg /* we don't want to fail here. */
145857a27c01Smrg #ifndef SMALL
1459ecadec7fSmrg if (fflag)
1460d55b8a21Sdsl goto lose;
146157a27c01Smrg #endif
1462668894b1Smrg if (rbytes == -1)
14632d5e05afSmrg maybe_warn("can't read %s", file);
1464668894b1Smrg else
146570e8425dSmrg goto unexpected_EOF;
1466d55b8a21Sdsl goto lose;
1467ecadec7fSmrg }
14685e22a92eSmrg infile_newdata(rbytes);
14694084ec54Smrg
14709e6cc36bSsimonb method = file_gettype(fourbytes);
14717149a529Smrg #ifndef SMALL
147299d2aa3dSmrg if (fflag == 0 && method == FT_UNKNOWN) {
147399d2aa3dSmrg maybe_warnx("%s: not in gzip format", file);
1474d55b8a21Sdsl goto lose;
1475ecadec7fSmrg }
1476ecadec7fSmrg
147757a27c01Smrg #endif
1478d55b8a21Sdsl
147911153dbcSmrg #ifndef SMALL
1480d55b8a21Sdsl if (method == FT_GZIP && Nflag) {
1481d55b8a21Sdsl unsigned char ts[4]; /* timestamp */
148211153dbcSmrg
148333c0ea44Smrg rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1484f143270cSlukem if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1485b1906bf1Smrg goto unexpected_EOF;
148633c0ea44Smrg if (rv == -1) {
148733c0ea44Smrg if (!fflag)
148811153dbcSmrg maybe_warn("can't read %s", file);
1489d55b8a21Sdsl goto lose;
14900bcef426Schristos }
14915e22a92eSmrg infile_newdata(rv);
1492d55b8a21Sdsl timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
149311153dbcSmrg
14949e6cc36bSsimonb if (fourbytes[3] & ORIG_NAME) {
1495d6eaf991Schristos rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
149699d2aa3dSmrg if (rbytes < 0) {
149799d2aa3dSmrg maybe_warn("can't read %s", file);
1498d55b8a21Sdsl goto lose;
149999d2aa3dSmrg }
1500d6eaf991Schristos if (name[0] != '\0') {
1501a1b1261aSmrg char *dp, *nf;
1502a1b1261aSmrg
1503d6eaf991Schristos /* Make sure that name is NUL-terminated */
1504d6eaf991Schristos name[rbytes] = '\0';
1505d6eaf991Schristos
1506a1b1261aSmrg /* strip saved directory name */
1507a1b1261aSmrg nf = strrchr(name, '/');
1508a1b1261aSmrg if (nf == NULL)
1509a1b1261aSmrg nf = name;
1510a1b1261aSmrg else
1511a1b1261aSmrg nf++;
1512a1b1261aSmrg
1513d55b8a21Sdsl /* preserve original directory name */
1514a1b1261aSmrg dp = strrchr(file, '/');
1515d55b8a21Sdsl if (dp == NULL)
1516d55b8a21Sdsl dp = file;
1517d55b8a21Sdsl else
1518d55b8a21Sdsl dp++;
1519d55b8a21Sdsl snprintf(outfile, outsize, "%.*s%.*s",
1520d6cee1d9She (int) (dp - file),
1521a1b1261aSmrg file, (int) rbytes, nf);
15224084ec54Smrg }
15234084ec54Smrg }
15244084ec54Smrg }
1525d55b8a21Sdsl #endif
1526d55b8a21Sdsl lseek(fd, 0, SEEK_SET);
15274084ec54Smrg
1528d4e27c8dSmrg if (cflag == 0 || lflag) {
1529d4e27c8dSmrg #ifndef SMALL
153017ebf9b5Stron if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
153108891561Smrg maybe_warnx("%s has %d other links -- skipping",
153208891561Smrg file, isb.st_nlink - 1);
15334084ec54Smrg goto lose;
15344084ec54Smrg }
153511153dbcSmrg if (nflag == 0 && timestamp)
153611153dbcSmrg isb.st_mtime = timestamp;
15371a05f328Smrg if (check_outfile(outfile) == 0)
153878b17b7bSmrg goto lose;
1539d55b8a21Sdsl #endif
15404084ec54Smrg }
154178b17b7bSmrg
15425e22a92eSmrg if (cflag)
15435e22a92eSmrg zfd = STDOUT_FILENO;
15445e22a92eSmrg else if (lflag)
15455e22a92eSmrg zfd = -1;
15465e22a92eSmrg else {
15472d5e05afSmrg zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1548d55b8a21Sdsl if (zfd == STDOUT_FILENO) {
1549d55b8a21Sdsl /* We won't close STDOUT_FILENO later... */
1550d55b8a21Sdsl zfd = dup(zfd);
1551d55b8a21Sdsl close(STDOUT_FILENO);
1552d55b8a21Sdsl }
15532d5e05afSmrg if (zfd == -1) {
15542d5e05afSmrg maybe_warn("can't open %s", outfile);
15552d5e05afSmrg goto lose;
15562d5e05afSmrg }
15575e22a92eSmrg }
15582d5e05afSmrg
155940b41259Schristos switch (method) {
1560b6e59003Stsutsui #ifndef NO_BZIP2_SUPPORT
156140b41259Schristos case FT_BZIP2:
156270da2f0fSmrg /* XXX */
15632d5e05afSmrg if (lflag) {
15642d5e05afSmrg maybe_warnx("no -l with bzip2 files");
156599d2aa3dSmrg goto lose;
156699d2aa3dSmrg }
1567ecadec7fSmrg
1568d55b8a21Sdsl size = unbzip2(fd, zfd, NULL, 0, NULL);
156940b41259Schristos break;
1570b6e59003Stsutsui #endif
1571ed20265fSmrg
1572b6e59003Stsutsui #ifndef NO_COMPRESS_SUPPORT
157340b41259Schristos case FT_Z: {
157491d1fa94Smrg FILE *in, *out;
157591d1fa94Smrg
157670da2f0fSmrg /* XXX */
15772d5e05afSmrg if (lflag) {
15782d5e05afSmrg maybe_warnx("no -l with Lempel-Ziv files");
15792d5e05afSmrg goto lose;
158091d1fa94Smrg }
15812d5e05afSmrg
1582d55b8a21Sdsl if ((in = zdopen(fd)) == NULL) {
1583d55b8a21Sdsl maybe_warn("zdopen for read: %s", file);
15842d5e05afSmrg goto lose;
15852d5e05afSmrg }
15862d5e05afSmrg
1587d55b8a21Sdsl out = fdopen(dup(zfd), "w");
15882d5e05afSmrg if (out == NULL) {
1589d55b8a21Sdsl maybe_warn("fdopen for write: %s", outfile);
1590d55b8a21Sdsl fclose(in);
15912d5e05afSmrg goto lose;
15922d5e05afSmrg }
159391d1fa94Smrg
15941d0e9794Smrg size = zuncompress(in, out, NULL, 0, NULL);
1595754b92f9Sdsl /* need to fclose() if ferror() is true... */
1596754b92f9Sdsl if (ferror(in) | fclose(in)) {
1597754b92f9Sdsl maybe_warn("failed infile fclose");
159899d2aa3dSmrg unlink(outfile);
15992d5e05afSmrg (void)fclose(out);
160099d2aa3dSmrg }
160199d2aa3dSmrg if (fclose(out) != 0) {
1602d55b8a21Sdsl maybe_warn("failed outfile fclose");
1603754b92f9Sdsl unlink(outfile);
16042d5e05afSmrg goto lose;
160591d1fa94Smrg }
160640b41259Schristos break;
160740b41259Schristos }
1608b6e59003Stsutsui #endif
160913e5b34eSmrg
1610569ceba5Smrg #ifndef NO_PACK_SUPPORT
161140b41259Schristos case FT_PACK:
1612569ceba5Smrg if (lflag) {
1613569ceba5Smrg maybe_warnx("no -l with packed files");
1614569ceba5Smrg goto lose;
1615569ceba5Smrg }
1616569ceba5Smrg
1617569ceba5Smrg size = unpack(fd, zfd, NULL, 0, NULL);
161840b41259Schristos break;
161940b41259Schristos #endif
162040b41259Schristos
162140b41259Schristos #ifndef NO_XZ_SUPPORT
162240b41259Schristos case FT_XZ:
162340b41259Schristos if (lflag) {
16243871bbe7Smartin size = unxz_len(fd);
16253871bbe7Smartin print_list_out(in_size, size, file);
16263871bbe7Smartin return -1;
162740b41259Schristos }
162840b41259Schristos size = unxz(fd, zfd, NULL, 0, NULL);
162940b41259Schristos break;
1630569ceba5Smrg #endif
1631569ceba5Smrg
163241c9b009Schristos #ifndef NO_LZ_SUPPORT
163341c9b009Schristos case FT_LZ:
163441c9b009Schristos if (lflag) {
163541c9b009Schristos maybe_warnx("no -l with lzip files");
163641c9b009Schristos goto lose;
163741c9b009Schristos }
163841c9b009Schristos size = unlz(fd, zfd, NULL, 0, NULL);
163941c9b009Schristos break;
164041c9b009Schristos #endif
164113e5b34eSmrg #ifndef SMALL
164240b41259Schristos case FT_UNKNOWN:
1643d55b8a21Sdsl if (lflag) {
1644d55b8a21Sdsl maybe_warnx("no -l for unknown filetypes");
164513e5b34eSmrg goto lose;
164613e5b34eSmrg }
1647d55b8a21Sdsl size = cat_fd(NULL, 0, NULL, fd);
164840b41259Schristos break;
164913e5b34eSmrg #endif
165040b41259Schristos default:
16511d0e9794Smrg if (lflag) {
16525e22a92eSmrg print_list(fd, in_size, outfile, isb.st_mtime);
1653d55b8a21Sdsl close(fd);
16545e6cd45bSyamt return -1; /* XXX */
165578b17b7bSmrg }
165678b17b7bSmrg
1657d55b8a21Sdsl size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
165840b41259Schristos break;
165999d2aa3dSmrg }
16604084ec54Smrg
1661d55b8a21Sdsl if (close(fd) != 0)
1662d55b8a21Sdsl maybe_warn("couldn't close input");
1663d55b8a21Sdsl if (zfd != STDOUT_FILENO && close(zfd) != 0)
1664d55b8a21Sdsl maybe_warn("couldn't close output");
1665d55b8a21Sdsl
16661d0e9794Smrg if (size == -1) {
1667d55b8a21Sdsl if (cflag == 0)
1668ecadec7fSmrg unlink(outfile);
1669d55b8a21Sdsl maybe_warnx("%s: uncompress failed", file);
1670d55b8a21Sdsl return -1;
167198445d24Smrg }
16724084ec54Smrg
16734084ec54Smrg /* if testing, or we uncompressed to stdout, this is all we need */
1674d7f0a538Smrg #ifndef SMALL
1675d7f0a538Smrg if (tflag)
1676d55b8a21Sdsl return size;
1677d7f0a538Smrg #endif
1678abc7a82eSdsl /* if we are uncompressing to stdin, don't remove the file. */
1679d7f0a538Smrg if (cflag)
1680d55b8a21Sdsl return size;
16814084ec54Smrg
16824084ec54Smrg /*
16834084ec54Smrg * if we create a file...
16844084ec54Smrg */
16854084ec54Smrg /*
1686abc7a82eSdsl * if we can't stat the file don't remove the file.
16874084ec54Smrg */
16881a05f328Smrg
16891a05f328Smrg ofd = open(outfile, O_RDWR, 0);
16901a05f328Smrg if (ofd == -1) {
16911a05f328Smrg maybe_warn("couldn't open (leaving original): %s",
16921a05f328Smrg outfile);
16931a05f328Smrg return -1;
16941a05f328Smrg }
16951a05f328Smrg if (fstat(ofd, &osb) != 0) {
16964084ec54Smrg maybe_warn("couldn't stat (leaving original): %s",
16974084ec54Smrg outfile);
16981a05f328Smrg close(ofd);
1699d55b8a21Sdsl return -1;
17004084ec54Smrg }
17014084ec54Smrg if (osb.st_size != size) {
1702cde2923dSmrg maybe_warnx("stat gave different size: %" PRIdOFF
1703d55b8a21Sdsl " != %" PRIdOFF " (leaving original)",
1704d55b8a21Sdsl size, osb.st_size);
17051a05f328Smrg close(ofd);
1706d55b8a21Sdsl unlink(outfile);
1707d55b8a21Sdsl return -1;
17084084ec54Smrg }
1709d55b8a21Sdsl unlink_input(file, &isb);
1710ed20265fSmrg #ifndef SMALL
17111a05f328Smrg copymodes(ofd, &isb, outfile);
1712ed20265fSmrg #endif
17131a05f328Smrg close(ofd);
1714d55b8a21Sdsl return size;
17154084ec54Smrg
171670e8425dSmrg unexpected_EOF:
171770e8425dSmrg maybe_warnx("%s: unexpected end of file", file);
17184084ec54Smrg lose:
1719d55b8a21Sdsl if (fd != -1)
1720d55b8a21Sdsl close(fd);
1721d55b8a21Sdsl if (zfd != -1 && zfd != STDOUT_FILENO)
1722d55b8a21Sdsl close(fd);
17232d5e05afSmrg return -1;
17244084ec54Smrg }
17254084ec54Smrg
1726d3697da1Schristos #ifndef check_siginfo
17275e22a92eSmrg static void
check_siginfo(void)17285e22a92eSmrg check_siginfo(void)
17295e22a92eSmrg {
173040ec67d2Schristos static int ttyfd = -2;
173140ec67d2Schristos char buf[2048];
173240ec67d2Schristos int n;
173340ec67d2Schristos
17345e22a92eSmrg if (print_info == 0)
17355e22a92eSmrg return;
17365e22a92eSmrg
173740ec67d2Schristos if (!infile)
173840ec67d2Schristos goto out;
173940ec67d2Schristos
174040ec67d2Schristos if (ttyfd == -2)
174140ec67d2Schristos ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC);
174240ec67d2Schristos
174340ec67d2Schristos if (ttyfd == -1)
174440ec67d2Schristos goto out;
174540ec67d2Schristos
174640ec67d2Schristos if (infile_total) {
174740ec67d2Schristos const double pcent = (100.0 * infile_current) / infile_total;
174840ec67d2Schristos
174940ec67d2Schristos n = snprintf(buf, sizeof(buf),
175040ec67d2Schristos "%s: %s: done %ju/%ju bytes (%3.2f%%)\n",
175140ec67d2Schristos getprogname(), infile, (uintmax_t)infile_current,
175240ec67d2Schristos (uintmax_t)infile_total, pcent);
175340ec67d2Schristos } else {
175440ec67d2Schristos n = snprintf(buf, sizeof(buf), "%s: %s: done %ju bytes\n",
175540ec67d2Schristos getprogname(), infile, (uintmax_t)infile_current);
17565e22a92eSmrg }
175740ec67d2Schristos
175840ec67d2Schristos if (n <= 0)
175940ec67d2Schristos goto out;
176040ec67d2Schristos
176140ec67d2Schristos write(ttyfd, buf, (size_t)n);
176240ec67d2Schristos out:
17635e22a92eSmrg print_info = 0;
17645e22a92eSmrg }
176501d7b234Schristos #endif
17665e22a92eSmrg
17671d0e9794Smrg static off_t
cat_fd(unsigned char * prepend,size_t count,off_t * gsizep,int fd)176813e5b34eSmrg cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
17691d0e9794Smrg {
17701d0e9794Smrg char buf[BUFLEN];
17711d0e9794Smrg off_t in_tot;
17721a05f328Smrg ssize_t w;
17731d0e9794Smrg
17741d0e9794Smrg in_tot = count;
177513a9a3bbSchristos w = write_retry(STDOUT_FILENO, prepend, count);
17761a05f328Smrg if (w == -1 || (size_t)w != count) {
17772d5e05afSmrg maybe_warn("write to stdout");
17782d5e05afSmrg return -1;
17792d5e05afSmrg }
17801d0e9794Smrg for (;;) {
17815c0ab9e0Smrg ssize_t rv;
17825c0ab9e0Smrg
178313e5b34eSmrg rv = read(fd, buf, sizeof buf);
178413e5b34eSmrg if (rv == 0)
178513e5b34eSmrg break;
178613e5b34eSmrg if (rv < 0) {
178713e5b34eSmrg maybe_warn("read from fd %d", fd);
178813e5b34eSmrg break;
178913e5b34eSmrg }
17905e22a92eSmrg infile_newdata(rv);
17911d0e9794Smrg
179213a9a3bbSchristos if (write_retry(STDOUT_FILENO, buf, rv) != rv) {
17932d5e05afSmrg maybe_warn("write to stdout");
179413e5b34eSmrg break;
17952d5e05afSmrg }
17961d0e9794Smrg in_tot += rv;
17971d0e9794Smrg }
17981d0e9794Smrg
17991d0e9794Smrg if (gsizep)
18001d0e9794Smrg *gsizep = in_tot;
18011d0e9794Smrg return (in_tot);
18021d0e9794Smrg }
18031d0e9794Smrg
18044084ec54Smrg static void
handle_stdin(void)18054084ec54Smrg handle_stdin(void)
18064084ec54Smrg {
18075e22a92eSmrg struct stat isb;
18089e6cc36bSsimonb unsigned char fourbytes[4];
18095e22a92eSmrg size_t in_size;
18101d0e9794Smrg off_t usize, gsize;
18111d0e9794Smrg enum filetype method;
1812751e975bSyamt ssize_t bytes_read;
18131d0e9794Smrg #ifndef NO_COMPRESS_SUPPORT
18141d0e9794Smrg FILE *in;
18151d0e9794Smrg #endif
18164084ec54Smrg
181757a27c01Smrg #ifndef SMALL
181878b17b7bSmrg if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
18194084ec54Smrg maybe_warnx("standard input is a terminal -- ignoring");
18205e22a92eSmrg goto out;
18214084ec54Smrg }
182257a27c01Smrg #endif
182378b17b7bSmrg
18242d5e05afSmrg if (fstat(STDIN_FILENO, &isb) < 0) {
18252d5e05afSmrg maybe_warn("fstat");
18265e22a92eSmrg goto out;
18272d5e05afSmrg }
18285e22a92eSmrg if (S_ISREG(isb.st_mode))
18295e22a92eSmrg in_size = isb.st_size;
18305e22a92eSmrg else
18315e22a92eSmrg in_size = 0;
18325e22a92eSmrg infile_set("(stdin)", in_size);
18335e22a92eSmrg
18345e22a92eSmrg if (lflag) {
18355e22a92eSmrg print_list(STDIN_FILENO, in_size, infile, isb.st_mtime);
18365e22a92eSmrg goto out;
183778b17b7bSmrg }
183878b17b7bSmrg
18399e6cc36bSsimonb bytes_read = read_retry(STDIN_FILENO, fourbytes, sizeof fourbytes);
1840751e975bSyamt if (bytes_read == -1) {
18412d5e05afSmrg maybe_warn("can't read stdin");
18425e22a92eSmrg goto out;
18439e6cc36bSsimonb } else if (bytes_read != sizeof(fourbytes)) {
184470e8425dSmrg maybe_warnx("(stdin): unexpected end of file");
18455e22a92eSmrg goto out;
18462d5e05afSmrg }
18471d0e9794Smrg
18489e6cc36bSsimonb method = file_gettype(fourbytes);
18491d0e9794Smrg switch (method) {
18501d0e9794Smrg default:
18511d0e9794Smrg #ifndef SMALL
18522d5e05afSmrg if (fflag == 0) {
18532d5e05afSmrg maybe_warnx("unknown compression format");
18545e22a92eSmrg goto out;
18552d5e05afSmrg }
18569e6cc36bSsimonb usize = cat_fd(fourbytes, sizeof fourbytes, &gsize, STDIN_FILENO);
18571d0e9794Smrg break;
18581d0e9794Smrg #endif
18591d0e9794Smrg case FT_GZIP:
18601d0e9794Smrg usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
18619e6cc36bSsimonb (char *)fourbytes, sizeof fourbytes, &gsize, "(stdin)");
18621d0e9794Smrg break;
18631d0e9794Smrg #ifndef NO_BZIP2_SUPPORT
18641d0e9794Smrg case FT_BZIP2:
18651d0e9794Smrg usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
18669e6cc36bSsimonb (char *)fourbytes, sizeof fourbytes, &gsize);
18671d0e9794Smrg break;
18681d0e9794Smrg #endif
18691d0e9794Smrg #ifndef NO_COMPRESS_SUPPORT
18701d0e9794Smrg case FT_Z:
1871d55b8a21Sdsl if ((in = zdopen(STDIN_FILENO)) == NULL) {
18722d5e05afSmrg maybe_warnx("zopen of stdin");
18735e22a92eSmrg goto out;
18742d5e05afSmrg }
18751d0e9794Smrg
18769e6cc36bSsimonb usize = zuncompress(in, stdout, (char *)fourbytes,
18779e6cc36bSsimonb sizeof fourbytes, &gsize);
1878d55b8a21Sdsl fclose(in);
18791d0e9794Smrg break;
18801d0e9794Smrg #endif
1881569ceba5Smrg #ifndef NO_PACK_SUPPORT
1882569ceba5Smrg case FT_PACK:
1883569ceba5Smrg usize = unpack(STDIN_FILENO, STDOUT_FILENO,
18849e6cc36bSsimonb (char *)fourbytes, sizeof fourbytes, &gsize);
1885569ceba5Smrg break;
1886569ceba5Smrg #endif
188740b41259Schristos #ifndef NO_XZ_SUPPORT
188840b41259Schristos case FT_XZ:
188940b41259Schristos usize = unxz(STDIN_FILENO, STDOUT_FILENO,
18909e6cc36bSsimonb (char *)fourbytes, sizeof fourbytes, &gsize);
189140b41259Schristos break;
189240b41259Schristos #endif
189341c9b009Schristos #ifndef NO_LZ_SUPPORT
189441c9b009Schristos case FT_LZ:
189541c9b009Schristos usize = unlz(STDIN_FILENO, STDOUT_FILENO,
18969e6cc36bSsimonb (char *)fourbytes, sizeof fourbytes, &gsize);
189741c9b009Schristos break;
189841c9b009Schristos #endif
18991d0e9794Smrg }
19001d0e9794Smrg
19011d0e9794Smrg #ifndef SMALL
19021d0e9794Smrg if (vflag && !tflag && usize != -1 && gsize != -1)
1903d55b8a21Sdsl print_verbage(NULL, NULL, usize, gsize);
1904e3969e9fSmrg if (vflag && tflag)
1905e3969e9fSmrg print_test("(stdin)", usize != -1);
19065e22a92eSmrg #else
19075e22a92eSmrg (void)&usize;
19081d0e9794Smrg #endif
19091d0e9794Smrg
19105e22a92eSmrg out:
19115e22a92eSmrg infile_clear();
19124084ec54Smrg }
19134084ec54Smrg
19144084ec54Smrg static void
handle_stdout(void)19154084ec54Smrg handle_stdout(void)
19164084ec54Smrg {
19175e22a92eSmrg off_t gsize;
19185e22a92eSmrg #ifndef SMALL
19195e22a92eSmrg off_t usize;
192040c07374Sjmc struct stat sb;
192140c07374Sjmc time_t systime;
192240c07374Sjmc uint32_t mtime;
192340c07374Sjmc int ret;
19244084ec54Smrg
192540ec67d2Schristos infile_set("<stdout>", 0);
19265e22a92eSmrg
19274084ec54Smrg if (fflag == 0 && isatty(STDOUT_FILENO)) {
19284084ec54Smrg maybe_warnx("standard output is a terminal -- ignoring");
19294084ec54Smrg return;
19304084ec54Smrg }
19315e22a92eSmrg
1932f0a7346dSsnj /* If stdin is a file use its mtime, otherwise use current time */
193340c07374Sjmc ret = fstat(STDIN_FILENO, &sb);
193440c07374Sjmc if (ret < 0) {
193540c07374Sjmc maybe_warn("Can't stat stdin");
193640c07374Sjmc return;
193740c07374Sjmc }
193840c07374Sjmc
19395e22a92eSmrg if (S_ISREG(sb.st_mode)) {
194040ec67d2Schristos infile_set("<stdout>", sb.st_size);
194140c07374Sjmc mtime = (uint32_t)sb.st_mtime;
19425e22a92eSmrg } else {
194340c07374Sjmc systime = time(NULL);
194440c07374Sjmc if (systime == -1) {
194540c07374Sjmc maybe_warn("time");
194640c07374Sjmc return;
194740c07374Sjmc }
194840c07374Sjmc mtime = (uint32_t)systime;
194940c07374Sjmc }
195040c07374Sjmc
19515e22a92eSmrg usize =
19525e22a92eSmrg #endif
19535e22a92eSmrg gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
195440c07374Sjmc #ifndef SMALL
19551d0e9794Smrg if (vflag && !tflag && usize != -1 && gsize != -1)
1956d55b8a21Sdsl print_verbage(NULL, NULL, usize, gsize);
19571d0e9794Smrg #endif
19584084ec54Smrg }
19594084ec54Smrg
19604084ec54Smrg /* do what is asked for, for the path name */
19614084ec54Smrg static void
handle_pathname(char * path)19624084ec54Smrg handle_pathname(char *path)
19634084ec54Smrg {
1964f8dc5986Sagc char *opath = path, *s = NULL;
19654084ec54Smrg ssize_t len;
1966d55b8a21Sdsl int slen;
19674084ec54Smrg struct stat sb;
19684084ec54Smrg
19694084ec54Smrg /* check for stdout/stdin */
19704084ec54Smrg if (path[0] == '-' && path[1] == '\0') {
19714084ec54Smrg if (dflag)
19724084ec54Smrg handle_stdin();
19734084ec54Smrg else
19744084ec54Smrg handle_stdout();
1975c6c68fa8Smrg return;
19764084ec54Smrg }
19774084ec54Smrg
19784084ec54Smrg retry:
1979cde2923dSmrg if (stat(path, &sb) != 0) {
19804084ec54Smrg /* lets try <path>.gz if we're decompressing */
1981f8dc5986Sagc if (dflag && s == NULL && errno == ENOENT) {
19824084ec54Smrg len = strlen(path);
1983d55b8a21Sdsl slen = suffixes[0].ziplen;
1984d55b8a21Sdsl s = malloc(len + slen + 1);
1985f8dc5986Sagc if (s == NULL)
19862d5e05afSmrg maybe_err("malloc");
1987d55b8a21Sdsl memcpy(s, path, len);
1988d55b8a21Sdsl memcpy(s + len, suffixes[0].zipped, slen + 1);
19894084ec54Smrg path = s;
19904084ec54Smrg goto retry;
19914084ec54Smrg }
19924084ec54Smrg maybe_warn("can't stat: %s", opath);
19934084ec54Smrg goto out;
19944084ec54Smrg }
19954084ec54Smrg
19964084ec54Smrg if (S_ISDIR(sb.st_mode)) {
199757a27c01Smrg #ifndef SMALL
19984084ec54Smrg if (rflag)
19995c0ab9e0Smrg handle_dir(path);
20004084ec54Smrg else
200157a27c01Smrg #endif
2002284b1b2aSyamt maybe_warnx("%s is a directory", path);
20034084ec54Smrg goto out;
20044084ec54Smrg }
20054084ec54Smrg
20064084ec54Smrg if (S_ISREG(sb.st_mode))
20074084ec54Smrg handle_file(path, &sb);
2008d55b8a21Sdsl else
2009d55b8a21Sdsl maybe_warnx("%s is not a regular file", path);
20104084ec54Smrg
20114084ec54Smrg out:
20124084ec54Smrg if (s)
20134084ec54Smrg free(s);
20144084ec54Smrg }
20154084ec54Smrg
20164084ec54Smrg /* compress/decompress a file */
20174084ec54Smrg static void
handle_file(char * file,struct stat * sbp)20184084ec54Smrg handle_file(char *file, struct stat *sbp)
20194084ec54Smrg {
2020d4e27c8dSmrg off_t usize, gsize;
20215fe88849Sagc char outfile[PATH_MAX];
20224084ec54Smrg
20235e22a92eSmrg infile_set(file, sbp->st_size);
20244084ec54Smrg if (dflag) {
20255fe88849Sagc usize = file_uncompress(file, outfile, sizeof(outfile));
2026b5d827ceShe #ifndef SMALL
2027e3969e9fSmrg if (vflag && tflag)
2028e3969e9fSmrg print_test(file, usize != -1);
2029b5d827ceShe #endif
20302d5e05afSmrg if (usize == -1)
20314084ec54Smrg return;
20324084ec54Smrg gsize = sbp->st_size;
20334084ec54Smrg } else {
20345fe88849Sagc gsize = file_compress(file, outfile, sizeof(outfile));
20352d5e05afSmrg if (gsize == -1)
20364084ec54Smrg return;
20374084ec54Smrg usize = sbp->st_size;
20384084ec54Smrg }
20395e22a92eSmrg infile_clear();
2040d7f0a538Smrg
2041d7f0a538Smrg #ifndef SMALL
20424084ec54Smrg if (vflag && !tflag)
20435fe88849Sagc print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
2044d7f0a538Smrg #endif
20454084ec54Smrg }
20464084ec54Smrg
204757a27c01Smrg #ifndef SMALL
2048d55b8a21Sdsl /* this is used with -r to recursively descend directories */
20494084ec54Smrg static void
handle_dir(char * dir)20505c0ab9e0Smrg handle_dir(char *dir)
20514084ec54Smrg {
20524084ec54Smrg char *path_argv[2];
20534084ec54Smrg FTS *fts;
20544084ec54Smrg FTSENT *entry;
20554084ec54Smrg
20564084ec54Smrg path_argv[0] = dir;
20574084ec54Smrg path_argv[1] = 0;
20583679c205Schristos fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
20594084ec54Smrg if (fts == NULL) {
20604084ec54Smrg warn("couldn't fts_open %s", dir);
20614084ec54Smrg return;
20624084ec54Smrg }
20634084ec54Smrg
20644084ec54Smrg while ((entry = fts_read(fts))) {
20654084ec54Smrg switch(entry->fts_info) {
20664084ec54Smrg case FTS_D:
20674084ec54Smrg case FTS_DP:
20684084ec54Smrg continue;
20694084ec54Smrg
20704084ec54Smrg case FTS_DNR:
20714084ec54Smrg case FTS_ERR:
20724084ec54Smrg case FTS_NS:
20734084ec54Smrg maybe_warn("%s", entry->fts_path);
20744084ec54Smrg continue;
20754084ec54Smrg case FTS_F:
20763679c205Schristos handle_file(entry->fts_path, entry->fts_statp);
20774084ec54Smrg }
20784084ec54Smrg }
20794084ec54Smrg (void)fts_close(fts);
20804084ec54Smrg }
208157a27c01Smrg #endif
20824084ec54Smrg
2083754b92f9Sdsl /* print a ratio - size reduction as a fraction of uncompressed size */
208488899e33Smrg static void
print_ratio(off_t in,off_t out,FILE * where)208588899e33Smrg print_ratio(off_t in, off_t out, FILE *where)
208688899e33Smrg {
2087754b92f9Sdsl int percent10; /* 10 * percent */
2088754b92f9Sdsl off_t diff;
2089754b92f9Sdsl char buff[8];
2090754b92f9Sdsl int len;
209188899e33Smrg
2092754b92f9Sdsl diff = in - out/2;
20935e22a92eSmrg if (in == 0 && out == 0)
20945e22a92eSmrg percent10 = 0;
20955e22a92eSmrg else if (diff < 0)
2096ff187e72Smrg /*
2097754b92f9Sdsl * Output is more than double size of input! print -99.9%
2098754b92f9Sdsl * Quite possibly we've failed to get the original size.
2099ff187e72Smrg */
2100754b92f9Sdsl percent10 = -999;
2101754b92f9Sdsl else {
2102754b92f9Sdsl /*
2103754b92f9Sdsl * We only need 12 bits of result from the final division,
2104754b92f9Sdsl * so reduce the values until a 32bit division will suffice.
2105754b92f9Sdsl */
2106754b92f9Sdsl while (in > 0x100000) {
2107754b92f9Sdsl diff >>= 1;
2108754b92f9Sdsl in >>= 1;
2109754b92f9Sdsl }
2110754b92f9Sdsl if (in != 0)
2111754b92f9Sdsl percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2112ff187e72Smrg else
2113754b92f9Sdsl percent10 = 0;
2114754b92f9Sdsl }
2115754b92f9Sdsl
2116754b92f9Sdsl len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2117754b92f9Sdsl /* Move the '.' to before the last digit */
2118754b92f9Sdsl buff[len - 1] = buff[len - 2];
2119754b92f9Sdsl buff[len - 2] = '.';
2120754b92f9Sdsl fprintf(where, "%5s%%", buff);
212188899e33Smrg }
212288899e33Smrg
2123d7f0a538Smrg #ifndef SMALL
21244084ec54Smrg /* print compression statistics, and the new name (if there is one!) */
21254084ec54Smrg static void
print_verbage(const char * file,const char * nfile,off_t usize,off_t gsize)2126d55b8a21Sdsl print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
21274084ec54Smrg {
21281d0e9794Smrg if (file)
212988899e33Smrg fprintf(stderr, "%s:%s ", file,
213088899e33Smrg strlen(file) < 7 ? "\t\t" : "\t");
2131d55b8a21Sdsl print_ratio(usize, gsize, stderr);
213275fa5559Smrg if (nfile)
213375fa5559Smrg fprintf(stderr, " -- replaced with %s", nfile);
21344084ec54Smrg fprintf(stderr, "\n");
21354084ec54Smrg fflush(stderr);
21364084ec54Smrg }
21374084ec54Smrg
21384084ec54Smrg /* print test results */
21394084ec54Smrg static void
print_test(const char * file,int ok)21405fe88849Sagc print_test(const char *file, int ok)
21414084ec54Smrg {
21424084ec54Smrg
21432d5e05afSmrg if (exit_value == 0 && ok == 0)
21442d5e05afSmrg exit_value = 1;
21454084ec54Smrg fprintf(stderr, "%s:%s %s\n", file,
21464084ec54Smrg strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
21474084ec54Smrg fflush(stderr);
21484084ec54Smrg }
2149d7f0a538Smrg #endif
21504084ec54Smrg
215178b17b7bSmrg /* print a file's info ala --list */
215278b17b7bSmrg /* eg:
215378b17b7bSmrg compressed uncompressed ratio uncompressed_name
215478b17b7bSmrg 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
215578b17b7bSmrg */
215678b17b7bSmrg static void
print_list(int fd,off_t out,const char * outfile,time_t ts)2157ff187e72Smrg print_list(int fd, off_t out, const char *outfile, time_t ts)
215878b17b7bSmrg {
215978b17b7bSmrg static int first = 1;
2160ed20265fSmrg #ifndef SMALL
216188899e33Smrg static off_t in_tot, out_tot;
21626b5252b1Slukem uint32_t crc = 0;
2163ed20265fSmrg #endif
21641a05f328Smrg off_t in = 0, rv;
216578b17b7bSmrg
21669e3ca570Smrg if (first) {
2167d7f0a538Smrg #ifndef SMALL
21689e3ca570Smrg if (vflag)
21699e3ca570Smrg printf("method crc date time ");
2170d7f0a538Smrg #endif
21719e3ca570Smrg if (qflag == 0)
21729e3ca570Smrg printf(" compressed uncompressed "
21739e3ca570Smrg "ratio uncompressed_name\n");
21749e3ca570Smrg }
217578b17b7bSmrg first = 0;
217678b17b7bSmrg
217788899e33Smrg /* print totals? */
2178ed20265fSmrg #ifndef SMALL
217988899e33Smrg if (fd == -1) {
218088899e33Smrg in = in_tot;
218188899e33Smrg out = out_tot;
2182ed20265fSmrg } else
2183ed20265fSmrg #endif
2184ed20265fSmrg {
218578b17b7bSmrg /* read the last 4 bytes - this is the uncompressed size */
21869e3ca570Smrg rv = lseek(fd, (off_t)(-8), SEEK_END);
218778b17b7bSmrg if (rv != -1) {
21889e3ca570Smrg unsigned char buf[8];
2189d55b8a21Sdsl uint32_t usize;
219078b17b7bSmrg
219170e8425dSmrg rv = read(fd, (char *)buf, sizeof(buf));
219270e8425dSmrg if (rv == -1)
21931d0e9794Smrg maybe_warn("read of uncompressed size");
219470e8425dSmrg else if (rv != sizeof(buf))
219570e8425dSmrg maybe_warnx("read of uncompressed size");
219670e8425dSmrg
219770e8425dSmrg else {
21982760f15bSkamil usize = buf[4];
21992760f15bSkamil usize |= (unsigned int)buf[5] << 8;
22002760f15bSkamil usize |= (unsigned int)buf[6] << 16;
22012760f15bSkamil usize |= (unsigned int)buf[7] << 24;
2202ff187e72Smrg in = (off_t)usize;
2203ed20265fSmrg #ifndef SMALL
22042760f15bSkamil crc = buf[0];
22052760f15bSkamil crc |= (unsigned int)buf[1] << 8;
22062760f15bSkamil crc |= (unsigned int)buf[2] << 16;
22072760f15bSkamil crc |= (unsigned int)buf[3] << 24;
2208ed20265fSmrg #endif
220978b17b7bSmrg }
221088899e33Smrg }
221170e8425dSmrg }
221278b17b7bSmrg
2213d7f0a538Smrg #ifndef SMALL
22149e3ca570Smrg if (vflag && fd == -1)
22159e3ca570Smrg printf(" ");
22169e3ca570Smrg else if (vflag) {
22179e3ca570Smrg char *date = ctime(&ts);
22189e3ca570Smrg
22199e3ca570Smrg /* skip the day, 1/100th second, and year */
22209e3ca570Smrg date += 4;
22219e3ca570Smrg date[12] = 0;
22229e3ca570Smrg printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
22239e3ca570Smrg }
2224ed20265fSmrg in_tot += in;
2225ed20265fSmrg out_tot += out;
2226d7f0a538Smrg #endif
22273871bbe7Smartin print_list_out(out, in, outfile);
22283871bbe7Smartin }
22293871bbe7Smartin
22303871bbe7Smartin static void
print_list_out(off_t out,off_t in,const char * outfile)22313871bbe7Smartin print_list_out(off_t out, off_t in, const char *outfile)
22323871bbe7Smartin {
2233ff187e72Smrg printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
223488899e33Smrg print_ratio(in, out, stdout);
223588899e33Smrg printf(" %s\n", outfile);
223678b17b7bSmrg }
223778b17b7bSmrg
22384084ec54Smrg /* display the usage of NetBSD gzip */
22394084ec54Smrg static void
usage(void)22404084ec54Smrg usage(void)
22414084ec54Smrg {
22424084ec54Smrg
22434084ec54Smrg fprintf(stderr, "%s\n", gzip_version);
22444084ec54Smrg fprintf(stderr,
2245ed20265fSmrg "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n"
224609e6110cSmrg #ifndef SMALL
22472f705835Swiz " -1 --fast fastest (worst) compression\n"
22482f705835Swiz " -2 .. -8 set compression level\n"
22492f705835Swiz " -9 --best best (slowest) compression\n"
22504084ec54Smrg " -c --stdout write to stdout, keep original files\n"
22514084ec54Smrg " --to-stdout\n"
22524084ec54Smrg " -d --decompress uncompress files\n"
22534084ec54Smrg " --uncompress\n"
22544084ec54Smrg " -f --force force overwriting & compress links\n"
22554084ec54Smrg " -h --help display this help\n"
225677a6c12fSmrg " -k --keep don't delete input files during operation\n"
22572f705835Swiz " -l --list list compressed file contents\n"
22584084ec54Smrg " -N --name save or restore original file name and time stamp\n"
22592f705835Swiz " -n --no-name don't save original file name or time stamp\n"
22604084ec54Smrg " -q --quiet output no warnings\n"
22614084ec54Smrg " -r --recursive recursively compress files in directories\n"
22624084ec54Smrg " -S .suf use suffix .suf instead of .gz\n"
22634084ec54Smrg " --suffix .suf\n"
22644084ec54Smrg " -t --test test compressed file\n"
22654084ec54Smrg " -V --version display program version\n"
22662f705835Swiz " -v --verbose print extra statistics\n",
226709e6110cSmrg #else
226809e6110cSmrg ,
226909e6110cSmrg #endif
22704084ec54Smrg getprogname());
22714084ec54Smrg exit(0);
22724084ec54Smrg }
22734084ec54Smrg
22744084ec54Smrg /* display the version of NetBSD gzip */
22754084ec54Smrg static void
display_version(void)22764084ec54Smrg display_version(void)
22774084ec54Smrg {
22784084ec54Smrg
22794084ec54Smrg fprintf(stderr, "%s\n", gzip_version);
22804084ec54Smrg exit(0);
22814084ec54Smrg }
2282ecadec7fSmrg
2283b6e59003Stsutsui #ifndef NO_BZIP2_SUPPORT
2284ecadec7fSmrg #include "unbzip2.c"
2285b6e59003Stsutsui #endif
2286b6e59003Stsutsui #ifndef NO_COMPRESS_SUPPORT
228791d1fa94Smrg #include "zuncompress.c"
2288b6e59003Stsutsui #endif
2289569ceba5Smrg #ifndef NO_PACK_SUPPORT
2290569ceba5Smrg #include "unpack.c"
2291569ceba5Smrg #endif
229240b41259Schristos #ifndef NO_XZ_SUPPORT
229340b41259Schristos #include "unxz.c"
229440b41259Schristos #endif
229541c9b009Schristos #ifndef NO_LZ_SUPPORT
229641c9b009Schristos #include "unlz.c"
229741c9b009Schristos #endif
2298751e975bSyamt
2299751e975bSyamt static ssize_t
read_retry(int fd,void * buf,size_t sz)2300751e975bSyamt read_retry(int fd, void *buf, size_t sz)
2301751e975bSyamt {
2302751e975bSyamt char *cp = buf;
23031a05f328Smrg size_t left = MIN(sz, (size_t) SSIZE_MAX);
2304751e975bSyamt
2305751e975bSyamt while (left > 0) {
2306751e975bSyamt ssize_t ret;
2307751e975bSyamt
23081a05f328Smrg ret = read(fd, cp, left);
2309751e975bSyamt if (ret == -1) {
2310751e975bSyamt return ret;
2311751e975bSyamt } else if (ret == 0) {
2312751e975bSyamt break; /* EOF */
2313751e975bSyamt }
2314751e975bSyamt cp += ret;
2315751e975bSyamt left -= ret;
2316751e975bSyamt }
2317751e975bSyamt
2318751e975bSyamt return sz - left;
2319751e975bSyamt }
232013a9a3bbSchristos
232113a9a3bbSchristos static ssize_t
write_retry(int fd,const void * buf,size_t sz)232213a9a3bbSchristos write_retry(int fd, const void *buf, size_t sz)
232313a9a3bbSchristos {
232413a9a3bbSchristos const char *cp = buf;
232513a9a3bbSchristos size_t left = MIN(sz, (size_t) SSIZE_MAX);
232613a9a3bbSchristos
232713a9a3bbSchristos while (left > 0) {
232813a9a3bbSchristos ssize_t ret;
232913a9a3bbSchristos
233013a9a3bbSchristos ret = write(fd, cp, left);
233113a9a3bbSchristos if (ret == -1) {
233213a9a3bbSchristos return ret;
233313a9a3bbSchristos } else if (ret == 0) {
233413a9a3bbSchristos abort(); /* Can't happen */
233513a9a3bbSchristos }
233613a9a3bbSchristos cp += ret;
233713a9a3bbSchristos left -= ret;
233813a9a3bbSchristos }
233913a9a3bbSchristos
234013a9a3bbSchristos return sz - left;
234113a9a3bbSchristos }
2342