xref: /netbsd-src/usr.bin/gzip/gzip.c (revision 74b93bff118ebb0ab3b384ea79fe9a7c9c62ae20)
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(&times[0], &sb.st_atimespec);
11311a05f328Smrg 	TIMESPEC_TO_TIMEVAL(&times[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