1226184Sdelphij/*	$NetBSD: gzip.c,v 1.105 2011/08/30 23:06:00 joerg Exp $	*/
2166255Sdelphij
3166255Sdelphij/*-
4166255Sdelphij * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
5166255Sdelphij * All rights reserved.
6166255Sdelphij *
7166255Sdelphij * Redistribution and use in source and binary forms, with or without
8166255Sdelphij * modification, are permitted provided that the following conditions
9166255Sdelphij * are met:
10166255Sdelphij * 1. Redistributions of source code must retain the above copyright
11166255Sdelphij *    notice, this list of conditions and the following disclaimer.
12166255Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
13166255Sdelphij *    notice, this list of conditions and the following disclaimer in the
14166255Sdelphij *    documentation and/or other materials provided with the distribution.
15166255Sdelphij *
16166255Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17166255Sdelphij * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18166255Sdelphij * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19166255Sdelphij * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20166255Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21166255Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22166255Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23166255Sdelphij * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24166255Sdelphij * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25166255Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26166255Sdelphij * SUCH DAMAGE.
27166255Sdelphij *
28166255Sdelphij */
29166255Sdelphij
30166255Sdelphij#include <sys/cdefs.h>
31166255Sdelphij#ifndef lint
32180727Sdelphij__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\
33180727Sdelphij Matthew R. Green.  All rights reserved.");
34222210Sdelphij__FBSDID("$FreeBSD$");
35166255Sdelphij#endif /* not lint */
36166255Sdelphij
37166255Sdelphij/*
38166255Sdelphij * gzip.c -- GPL free gzip using zlib.
39166255Sdelphij *
40166255Sdelphij * RFC 1950 covers the zlib format
41166255Sdelphij * RFC 1951 covers the deflate format
42166255Sdelphij * RFC 1952 covers the gzip format
43166255Sdelphij *
44166255Sdelphij * TODO:
45166255Sdelphij *	- use mmap where possible
46166255Sdelphij *	- make bzip2/compress -v/-t/-l support work as well as possible
47166255Sdelphij */
48166255Sdelphij
49166255Sdelphij#include <sys/param.h>
50166255Sdelphij#include <sys/stat.h>
51166255Sdelphij#include <sys/time.h>
52166255Sdelphij
53166255Sdelphij#include <inttypes.h>
54166255Sdelphij#include <unistd.h>
55166255Sdelphij#include <stdio.h>
56166255Sdelphij#include <string.h>
57166255Sdelphij#include <stdlib.h>
58166255Sdelphij#include <err.h>
59166255Sdelphij#include <errno.h>
60166255Sdelphij#include <fcntl.h>
61166255Sdelphij#include <zlib.h>
62166255Sdelphij#include <fts.h>
63166255Sdelphij#include <libgen.h>
64166255Sdelphij#include <stdarg.h>
65166255Sdelphij#include <getopt.h>
66166255Sdelphij#include <time.h>
67166255Sdelphij
68166255Sdelphij/* what type of file are we dealing with */
69166255Sdelphijenum filetype {
70166255Sdelphij	FT_GZIP,
71166255Sdelphij#ifndef NO_BZIP2_SUPPORT
72166255Sdelphij	FT_BZIP2,
73166255Sdelphij#endif
74166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
75166255Sdelphij	FT_Z,
76166255Sdelphij#endif
77194579Sdelphij#ifndef NO_PACK_SUPPORT
78194579Sdelphij	FT_PACK,
79194579Sdelphij#endif
80226184Sdelphij#ifndef NO_XZ_SUPPORT
81226184Sdelphij	FT_XZ,
82226184Sdelphij#endif
83166255Sdelphij	FT_LAST,
84166255Sdelphij	FT_UNKNOWN
85166255Sdelphij};
86166255Sdelphij
87166255Sdelphij#ifndef NO_BZIP2_SUPPORT
88166255Sdelphij#include <bzlib.h>
89166255Sdelphij
90166255Sdelphij#define BZ2_SUFFIX	".bz2"
91166255Sdelphij#define BZIP2_MAGIC	"\102\132\150"
92166255Sdelphij#endif
93166255Sdelphij
94166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
95166255Sdelphij#define Z_SUFFIX	".Z"
96166255Sdelphij#define Z_MAGIC		"\037\235"
97166255Sdelphij#endif
98166255Sdelphij
99194579Sdelphij#ifndef NO_PACK_SUPPORT
100194579Sdelphij#define PACK_MAGIC	"\037\036"
101194579Sdelphij#endif
102194579Sdelphij
103226184Sdelphij#ifndef NO_XZ_SUPPORT
104226184Sdelphij#include <lzma.h>
105226184Sdelphij#define XZ_SUFFIX	".xz"
106226184Sdelphij#define XZ_MAGIC	"\3757zXZ"
107226184Sdelphij#endif
108226184Sdelphij
109166255Sdelphij#define GZ_SUFFIX	".gz"
110166255Sdelphij
111166255Sdelphij#define BUFLEN		(64 * 1024)
112166255Sdelphij
113166255Sdelphij#define GZIP_MAGIC0	0x1F
114166255Sdelphij#define GZIP_MAGIC1	0x8B
115166255Sdelphij#define GZIP_OMAGIC1	0x9E
116166255Sdelphij
117166255Sdelphij#define GZIP_TIMESTAMP	(off_t)4
118166255Sdelphij#define GZIP_ORIGNAME	(off_t)10
119166255Sdelphij
120166255Sdelphij#define HEAD_CRC	0x02
121166255Sdelphij#define EXTRA_FIELD	0x04
122166255Sdelphij#define ORIG_NAME	0x08
123166255Sdelphij#define COMMENT		0x10
124166255Sdelphij
125166255Sdelphij#define OS_CODE		3	/* Unix */
126166255Sdelphij
127166255Sdelphijtypedef struct {
128166255Sdelphij    const char	*zipped;
129166255Sdelphij    int		ziplen;
130166255Sdelphij    const char	*normal;	/* for unzip - must not be longer than zipped */
131166255Sdelphij} suffixes_t;
132166255Sdelphijstatic suffixes_t suffixes[] = {
133166255Sdelphij#define	SUFFIX(Z, N) {Z, sizeof Z - 1, N}
134166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S .xxx */
135166255Sdelphij#ifndef SMALL
136166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),
137166255Sdelphij	SUFFIX(".z",		""),
138166255Sdelphij	SUFFIX("-gz",		""),
139166255Sdelphij	SUFFIX("-z",		""),
140166255Sdelphij	SUFFIX("_z",		""),
141166255Sdelphij	SUFFIX(".taz",		".tar"),
142166255Sdelphij	SUFFIX(".tgz",		".tar"),
143166255Sdelphij#ifndef NO_BZIP2_SUPPORT
144166255Sdelphij	SUFFIX(BZ2_SUFFIX,	""),
145176980Srwatson	SUFFIX(".tbz",		".tar"),
146176980Srwatson	SUFFIX(".tbz2",		".tar"),
147166255Sdelphij#endif
148166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
149166255Sdelphij	SUFFIX(Z_SUFFIX,	""),
150166255Sdelphij#endif
151226184Sdelphij#ifndef NO_XZ_SUPPORT
152226184Sdelphij	SUFFIX(XZ_SUFFIX,	""),
153226184Sdelphij#endif
154166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S "" */
155166255Sdelphij#endif /* SMALL */
156166255Sdelphij#undef SUFFIX
157166255Sdelphij};
158166255Sdelphij#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
159195988Sdelphij#define SUFFIX_MAXLEN	30
160195988Sdelphij
161226184Sdelphijstatic	const char	gzip_version[] = "FreeBSD gzip 20111009";
162166255Sdelphij
163166255Sdelphij#ifndef SMALL
164166255Sdelphijstatic	const char	gzip_copyright[] = \
165166255Sdelphij"   Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
166166255Sdelphij"   All rights reserved.\n"
167166255Sdelphij"\n"
168166255Sdelphij"   Redistribution and use in source and binary forms, with or without\n"
169166255Sdelphij"   modification, are permitted provided that the following conditions\n"
170166255Sdelphij"   are met:\n"
171166255Sdelphij"   1. Redistributions of source code must retain the above copyright\n"
172166255Sdelphij"      notice, this list of conditions and the following disclaimer.\n"
173166255Sdelphij"   2. Redistributions in binary form must reproduce the above copyright\n"
174166255Sdelphij"      notice, this list of conditions and the following disclaimer in the\n"
175166255Sdelphij"      documentation and/or other materials provided with the distribution.\n"
176166255Sdelphij"\n"
177166255Sdelphij"   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
178166255Sdelphij"   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
179166255Sdelphij"   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
180166255Sdelphij"   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
181166255Sdelphij"   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
182166255Sdelphij"   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
183166255Sdelphij"   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
184166255Sdelphij"   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
185166255Sdelphij"   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
186166255Sdelphij"   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
187166255Sdelphij"   SUCH DAMAGE.";
188166255Sdelphij#endif
189166255Sdelphij
190166255Sdelphijstatic	int	cflag;			/* stdout mode */
191166255Sdelphijstatic	int	dflag;			/* decompress mode */
192166255Sdelphijstatic	int	lflag;			/* list mode */
193166255Sdelphijstatic	int	numflag = 6;		/* gzip -1..-9 value */
194166255Sdelphij
195166255Sdelphij#ifndef SMALL
196166255Sdelphijstatic	int	fflag;			/* force mode */
197170053Sdelphijstatic	int	kflag;			/* don't delete input files */
198166255Sdelphijstatic	int	nflag;			/* don't save name/timestamp */
199166255Sdelphijstatic	int	Nflag;			/* don't restore name/timestamp */
200166255Sdelphijstatic	int	qflag;			/* quiet mode */
201166255Sdelphijstatic	int	rflag;			/* recursive mode */
202166255Sdelphijstatic	int	tflag;			/* test */
203166255Sdelphijstatic	int	vflag;			/* verbose mode */
204207247Sdelphijstatic	const char *remove_file = NULL;	/* file to be removed upon SIGINT */
205166255Sdelphij#else
206166255Sdelphij#define		qflag	0
207166255Sdelphij#define		tflag	0
208166255Sdelphij#endif
209166255Sdelphij
210166255Sdelphijstatic	int	exit_value = 0;		/* exit value */
211166255Sdelphij
212166255Sdelphijstatic	char	*infile;		/* name of file coming in */
213166255Sdelphij
214226184Sdelphijstatic	void	maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
215226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
216226184Sdelphij    !defined(NO_XZ_SUPPORT)
217226184Sdelphijstatic	void	maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
218166255Sdelphij#endif
219226184Sdelphijstatic	void	maybe_warn(const char *fmt, ...) __printflike(1, 2);
220226184Sdelphijstatic	void	maybe_warnx(const char *fmt, ...) __printflike(1, 2);
221166255Sdelphijstatic	enum filetype file_gettype(u_char *);
222166255Sdelphij#ifdef SMALL
223166255Sdelphij#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
224166255Sdelphij#endif
225166255Sdelphijstatic	off_t	gz_compress(int, int, off_t *, const char *, uint32_t);
226166255Sdelphijstatic	off_t	gz_uncompress(int, int, char *, size_t, off_t *, const char *);
227166255Sdelphijstatic	off_t	file_compress(char *, char *, size_t);
228166255Sdelphijstatic	off_t	file_uncompress(char *, char *, size_t);
229166255Sdelphijstatic	void	handle_pathname(char *);
230166255Sdelphijstatic	void	handle_file(char *, struct stat *);
231166255Sdelphijstatic	void	handle_stdin(void);
232166255Sdelphijstatic	void	handle_stdout(void);
233166255Sdelphijstatic	void	print_ratio(off_t, off_t, FILE *);
234166255Sdelphijstatic	void	print_list(int fd, off_t, const char *, time_t);
235226184Sdelphijstatic	void	usage(void) __dead2;
236226184Sdelphijstatic	void	display_version(void) __dead2;
237166255Sdelphij#ifndef SMALL
238166255Sdelphijstatic	void	display_license(void);
239207247Sdelphijstatic	void	sigint_handler(int);
240166255Sdelphij#endif
241166255Sdelphijstatic	const suffixes_t *check_suffix(char *, int);
242166255Sdelphijstatic	ssize_t	read_retry(int, void *, size_t);
243166255Sdelphij
244166255Sdelphij#ifdef SMALL
245166255Sdelphij#define unlink_input(f, sb) unlink(f)
246166255Sdelphij#else
247166255Sdelphijstatic	off_t	cat_fd(unsigned char *, size_t, off_t *, int fd);
248166255Sdelphijstatic	void	prepend_gzip(char *, int *, char ***);
249166255Sdelphijstatic	void	handle_dir(char *);
250166255Sdelphijstatic	void	print_verbage(const char *, const char *, off_t, off_t);
251166255Sdelphijstatic	void	print_test(const char *, int);
252166255Sdelphijstatic	void	copymodes(int fd, const struct stat *, const char *file);
253166255Sdelphijstatic	int	check_outfile(const char *outfile);
254166255Sdelphij#endif
255166255Sdelphij
256166255Sdelphij#ifndef NO_BZIP2_SUPPORT
257166255Sdelphijstatic	off_t	unbzip2(int, int, char *, size_t, off_t *);
258166255Sdelphij#endif
259166255Sdelphij
260166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
261166255Sdelphijstatic	FILE 	*zdopen(int);
262166255Sdelphijstatic	off_t	zuncompress(FILE *, FILE *, char *, size_t, off_t *);
263166255Sdelphij#endif
264166255Sdelphij
265194579Sdelphij#ifndef NO_PACK_SUPPORT
266194579Sdelphijstatic	off_t	unpack(int, int, char *, size_t, off_t *);
267194579Sdelphij#endif
268194579Sdelphij
269226184Sdelphij#ifndef NO_XZ_SUPPORT
270226184Sdelphijstatic	off_t	unxz(int, int, char *, size_t, off_t *);
271226184Sdelphij#endif
272166255Sdelphij
273166255Sdelphij#ifdef SMALL
274166255Sdelphij#define getopt_long(a,b,c,d,e) getopt(a,b,c)
275166255Sdelphij#else
276166255Sdelphijstatic const struct option longopts[] = {
277166255Sdelphij	{ "stdout",		no_argument,		0,	'c' },
278166255Sdelphij	{ "to-stdout",		no_argument,		0,	'c' },
279166255Sdelphij	{ "decompress",		no_argument,		0,	'd' },
280166255Sdelphij	{ "uncompress",		no_argument,		0,	'd' },
281166255Sdelphij	{ "force",		no_argument,		0,	'f' },
282166255Sdelphij	{ "help",		no_argument,		0,	'h' },
283170053Sdelphij	{ "keep",		no_argument,		0,	'k' },
284166255Sdelphij	{ "list",		no_argument,		0,	'l' },
285166255Sdelphij	{ "no-name",		no_argument,		0,	'n' },
286166255Sdelphij	{ "name",		no_argument,		0,	'N' },
287166255Sdelphij	{ "quiet",		no_argument,		0,	'q' },
288166255Sdelphij	{ "recursive",		no_argument,		0,	'r' },
289166255Sdelphij	{ "suffix",		required_argument,	0,	'S' },
290166255Sdelphij	{ "test",		no_argument,		0,	't' },
291166255Sdelphij	{ "verbose",		no_argument,		0,	'v' },
292166255Sdelphij	{ "version",		no_argument,		0,	'V' },
293166255Sdelphij	{ "fast",		no_argument,		0,	'1' },
294166255Sdelphij	{ "best",		no_argument,		0,	'9' },
295166255Sdelphij	{ "ascii",		no_argument,		0,	'a' },
296166255Sdelphij	{ "license",		no_argument,		0,	'L' },
297166255Sdelphij	{ NULL,			no_argument,		0,	0 },
298166255Sdelphij};
299166255Sdelphij#endif
300166255Sdelphij
301166255Sdelphijint
302166255Sdelphijmain(int argc, char **argv)
303166255Sdelphij{
304166255Sdelphij	const char *progname = getprogname();
305166255Sdelphij#ifndef SMALL
306166255Sdelphij	char *gzip;
307166255Sdelphij	int len;
308166255Sdelphij#endif
309166255Sdelphij	int ch;
310166255Sdelphij
311166255Sdelphij#ifndef SMALL
312166255Sdelphij	if ((gzip = getenv("GZIP")) != NULL)
313166255Sdelphij		prepend_gzip(gzip, &argc, &argv);
314207247Sdelphij	signal(SIGINT, sigint_handler);
315166255Sdelphij#endif
316166255Sdelphij
317166255Sdelphij	/*
318166255Sdelphij	 * XXX
319166255Sdelphij	 * handle being called `gunzip', `zcat' and `gzcat'
320166255Sdelphij	 */
321166255Sdelphij	if (strcmp(progname, "gunzip") == 0)
322166255Sdelphij		dflag = 1;
323166255Sdelphij	else if (strcmp(progname, "zcat") == 0 ||
324166255Sdelphij		 strcmp(progname, "gzcat") == 0)
325166255Sdelphij		dflag = cflag = 1;
326166255Sdelphij
327166255Sdelphij#ifdef SMALL
328222210Sdelphij#define OPT_LIST "123456789cdhlV"
329166255Sdelphij#else
330170053Sdelphij#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
331166255Sdelphij#endif
332166255Sdelphij
333166255Sdelphij	while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
334166255Sdelphij		switch (ch) {
335166255Sdelphij		case '1': case '2': case '3':
336166255Sdelphij		case '4': case '5': case '6':
337166255Sdelphij		case '7': case '8': case '9':
338166255Sdelphij			numflag = ch - '0';
339166255Sdelphij			break;
340166255Sdelphij		case 'c':
341166255Sdelphij			cflag = 1;
342166255Sdelphij			break;
343166255Sdelphij		case 'd':
344166255Sdelphij			dflag = 1;
345166255Sdelphij			break;
346166255Sdelphij		case 'l':
347166255Sdelphij			lflag = 1;
348166255Sdelphij			dflag = 1;
349166255Sdelphij			break;
350166255Sdelphij		case 'V':
351166255Sdelphij			display_version();
352166255Sdelphij			/* NOTREACHED */
353166255Sdelphij#ifndef SMALL
354166255Sdelphij		case 'a':
355166255Sdelphij			fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
356166255Sdelphij			break;
357166255Sdelphij		case 'f':
358166255Sdelphij			fflag = 1;
359166255Sdelphij			break;
360170053Sdelphij		case 'k':
361170053Sdelphij			kflag = 1;
362170053Sdelphij			break;
363166255Sdelphij		case 'L':
364166255Sdelphij			display_license();
365166255Sdelphij			/* NOT REACHED */
366166255Sdelphij		case 'N':
367166255Sdelphij			nflag = 0;
368166255Sdelphij			Nflag = 1;
369166255Sdelphij			break;
370166255Sdelphij		case 'n':
371166255Sdelphij			nflag = 1;
372166255Sdelphij			Nflag = 0;
373166255Sdelphij			break;
374166255Sdelphij		case 'q':
375166255Sdelphij			qflag = 1;
376166255Sdelphij			break;
377166255Sdelphij		case 'r':
378166255Sdelphij			rflag = 1;
379166255Sdelphij			break;
380166255Sdelphij		case 'S':
381166255Sdelphij			len = strlen(optarg);
382166255Sdelphij			if (len != 0) {
383195988Sdelphij				if (len > SUFFIX_MAXLEN)
384195988Sdelphij					errx(1, "incorrect suffix: '%s': too long", optarg);
385166255Sdelphij				suffixes[0].zipped = optarg;
386166255Sdelphij				suffixes[0].ziplen = len;
387166255Sdelphij			} else {
388166255Sdelphij				suffixes[NUM_SUFFIXES - 1].zipped = "";
389166255Sdelphij				suffixes[NUM_SUFFIXES - 1].ziplen = 0;
390166255Sdelphij			}
391166255Sdelphij			break;
392166255Sdelphij		case 't':
393166255Sdelphij			cflag = 1;
394166255Sdelphij			tflag = 1;
395166255Sdelphij			dflag = 1;
396166255Sdelphij			break;
397166255Sdelphij		case 'v':
398166255Sdelphij			vflag = 1;
399166255Sdelphij			break;
400166255Sdelphij#endif
401166255Sdelphij		default:
402166255Sdelphij			usage();
403166255Sdelphij			/* NOTREACHED */
404166255Sdelphij		}
405166255Sdelphij	}
406166255Sdelphij	argv += optind;
407166255Sdelphij	argc -= optind;
408166255Sdelphij
409166255Sdelphij	if (argc == 0) {
410166255Sdelphij		if (dflag)	/* stdin mode */
411166255Sdelphij			handle_stdin();
412166255Sdelphij		else		/* stdout mode */
413166255Sdelphij			handle_stdout();
414166255Sdelphij	} else {
415166255Sdelphij		do {
416166255Sdelphij			handle_pathname(argv[0]);
417166255Sdelphij		} while (*++argv);
418166255Sdelphij	}
419166255Sdelphij#ifndef SMALL
420166255Sdelphij	if (qflag == 0 && lflag && argc > 1)
421166255Sdelphij		print_list(-1, 0, "(totals)", 0);
422166255Sdelphij#endif
423166255Sdelphij	exit(exit_value);
424166255Sdelphij}
425166255Sdelphij
426166255Sdelphij/* maybe print a warning */
427166255Sdelphijvoid
428166255Sdelphijmaybe_warn(const char *fmt, ...)
429166255Sdelphij{
430166255Sdelphij	va_list ap;
431166255Sdelphij
432166255Sdelphij	if (qflag == 0) {
433166255Sdelphij		va_start(ap, fmt);
434166255Sdelphij		vwarn(fmt, ap);
435166255Sdelphij		va_end(ap);
436166255Sdelphij	}
437166255Sdelphij	if (exit_value == 0)
438166255Sdelphij		exit_value = 1;
439166255Sdelphij}
440166255Sdelphij
441166255Sdelphij/* ... without an errno. */
442166255Sdelphijvoid
443166255Sdelphijmaybe_warnx(const char *fmt, ...)
444166255Sdelphij{
445166255Sdelphij	va_list ap;
446166255Sdelphij
447166255Sdelphij	if (qflag == 0) {
448166255Sdelphij		va_start(ap, fmt);
449166255Sdelphij		vwarnx(fmt, ap);
450166255Sdelphij		va_end(ap);
451166255Sdelphij	}
452166255Sdelphij	if (exit_value == 0)
453166255Sdelphij		exit_value = 1;
454166255Sdelphij}
455166255Sdelphij
456166255Sdelphij/* maybe print an error */
457166255Sdelphijvoid
458166255Sdelphijmaybe_err(const char *fmt, ...)
459166255Sdelphij{
460166255Sdelphij	va_list ap;
461166255Sdelphij
462166255Sdelphij	if (qflag == 0) {
463166255Sdelphij		va_start(ap, fmt);
464166255Sdelphij		vwarn(fmt, ap);
465166255Sdelphij		va_end(ap);
466166255Sdelphij	}
467166255Sdelphij	exit(2);
468166255Sdelphij}
469166255Sdelphij
470226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
471226184Sdelphij    !defined(NO_XZ_SUPPORT)
472166255Sdelphij/* ... without an errno. */
473166255Sdelphijvoid
474166255Sdelphijmaybe_errx(const char *fmt, ...)
475166255Sdelphij{
476166255Sdelphij	va_list ap;
477166255Sdelphij
478166255Sdelphij	if (qflag == 0) {
479166255Sdelphij		va_start(ap, fmt);
480166255Sdelphij		vwarnx(fmt, ap);
481166255Sdelphij		va_end(ap);
482166255Sdelphij	}
483166255Sdelphij	exit(2);
484166255Sdelphij}
485166255Sdelphij#endif
486166255Sdelphij
487166255Sdelphij#ifndef SMALL
488166255Sdelphij/* split up $GZIP and prepend it to the argument list */
489166255Sdelphijstatic void
490166255Sdelphijprepend_gzip(char *gzip, int *argc, char ***argv)
491166255Sdelphij{
492166255Sdelphij	char *s, **nargv, **ac;
493166255Sdelphij	int nenvarg = 0, i;
494166255Sdelphij
495166255Sdelphij	/* scan how many arguments there are */
496166255Sdelphij	for (s = gzip;;) {
497166255Sdelphij		while (*s == ' ' || *s == '\t')
498166255Sdelphij			s++;
499166255Sdelphij		if (*s == 0)
500166255Sdelphij			goto count_done;
501166255Sdelphij		nenvarg++;
502166255Sdelphij		while (*s != ' ' && *s != '\t')
503166255Sdelphij			if (*s++ == 0)
504166255Sdelphij				goto count_done;
505166255Sdelphij	}
506166255Sdelphijcount_done:
507166255Sdelphij	/* punt early */
508166255Sdelphij	if (nenvarg == 0)
509166255Sdelphij		return;
510166255Sdelphij
511166255Sdelphij	*argc += nenvarg;
512166255Sdelphij	ac = *argv;
513166255Sdelphij
514166255Sdelphij	nargv = (char **)malloc((*argc + 1) * sizeof(char *));
515166255Sdelphij	if (nargv == NULL)
516166255Sdelphij		maybe_err("malloc");
517166255Sdelphij
518166255Sdelphij	/* stash this away */
519166255Sdelphij	*argv = nargv;
520166255Sdelphij
521166255Sdelphij	/* copy the program name first */
522166255Sdelphij	i = 0;
523166255Sdelphij	nargv[i++] = *(ac++);
524166255Sdelphij
525166255Sdelphij	/* take a copy of $GZIP and add it to the array */
526166255Sdelphij	s = strdup(gzip);
527166255Sdelphij	if (s == NULL)
528166255Sdelphij		maybe_err("strdup");
529166255Sdelphij	for (;;) {
530166255Sdelphij		/* Skip whitespaces. */
531166255Sdelphij		while (*s == ' ' || *s == '\t')
532166255Sdelphij			s++;
533166255Sdelphij		if (*s == 0)
534166255Sdelphij			goto copy_done;
535166255Sdelphij		nargv[i++] = s;
536166255Sdelphij		/* Find the end of this argument. */
537166255Sdelphij		while (*s != ' ' && *s != '\t')
538166255Sdelphij			if (*s++ == 0)
539166255Sdelphij				/* Argument followed by NUL. */
540166255Sdelphij				goto copy_done;
541166255Sdelphij		/* Terminate by overwriting ' ' or '\t' with NUL. */
542166255Sdelphij		*s++ = 0;
543166255Sdelphij	}
544166255Sdelphijcopy_done:
545166255Sdelphij
546166255Sdelphij	/* copy the original arguments and a NULL */
547166255Sdelphij	while (*ac)
548166255Sdelphij		nargv[i++] = *(ac++);
549166255Sdelphij	nargv[i] = NULL;
550166255Sdelphij}
551166255Sdelphij#endif
552166255Sdelphij
553166255Sdelphij/* compress input to output. Return bytes read, -1 on error */
554166255Sdelphijstatic off_t
555166255Sdelphijgz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
556166255Sdelphij{
557166255Sdelphij	z_stream z;
558166255Sdelphij	char *outbufp, *inbufp;
559166255Sdelphij	off_t in_tot = 0, out_tot = 0;
560166255Sdelphij	ssize_t in_size;
561166255Sdelphij	int i, error;
562166255Sdelphij	uLong crc;
563166255Sdelphij#ifdef SMALL
564166255Sdelphij	static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
565166255Sdelphij				 0, 0, 0, 0,
566166255Sdelphij				 0, OS_CODE };
567166255Sdelphij#endif
568166255Sdelphij
569166255Sdelphij	outbufp = malloc(BUFLEN);
570166255Sdelphij	inbufp = malloc(BUFLEN);
571166255Sdelphij	if (outbufp == NULL || inbufp == NULL) {
572166255Sdelphij		maybe_err("malloc failed");
573166255Sdelphij		goto out;
574166255Sdelphij	}
575166255Sdelphij
576166255Sdelphij	memset(&z, 0, sizeof z);
577166255Sdelphij	z.zalloc = Z_NULL;
578166255Sdelphij	z.zfree = Z_NULL;
579166255Sdelphij	z.opaque = 0;
580166255Sdelphij
581166255Sdelphij#ifdef SMALL
582166255Sdelphij	memcpy(outbufp, header, sizeof header);
583166255Sdelphij	i = sizeof header;
584166255Sdelphij#else
585166255Sdelphij	if (nflag != 0) {
586166255Sdelphij		mtime = 0;
587166255Sdelphij		origname = "";
588166255Sdelphij	}
589166255Sdelphij
590166255Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
591166255Sdelphij		     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
592166255Sdelphij		     *origname ? ORIG_NAME : 0,
593166255Sdelphij		     mtime & 0xff,
594166255Sdelphij		     (mtime >> 8) & 0xff,
595166255Sdelphij		     (mtime >> 16) & 0xff,
596166255Sdelphij		     (mtime >> 24) & 0xff,
597166255Sdelphij		     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
598166255Sdelphij		     OS_CODE, origname);
599166255Sdelphij	if (i >= BUFLEN)
600166255Sdelphij		/* this need PATH_MAX > BUFLEN ... */
601166255Sdelphij		maybe_err("snprintf");
602166255Sdelphij	if (*origname)
603166255Sdelphij		i++;
604166255Sdelphij#endif
605166255Sdelphij
606166255Sdelphij	z.next_out = (unsigned char *)outbufp + i;
607166255Sdelphij	z.avail_out = BUFLEN - i;
608166255Sdelphij
609166255Sdelphij	error = deflateInit2(&z, numflag, Z_DEFLATED,
610166255Sdelphij			     (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
611166255Sdelphij	if (error != Z_OK) {
612166255Sdelphij		maybe_warnx("deflateInit2 failed");
613166255Sdelphij		in_tot = -1;
614166255Sdelphij		goto out;
615166255Sdelphij	}
616166255Sdelphij
617166255Sdelphij	crc = crc32(0L, Z_NULL, 0);
618166255Sdelphij	for (;;) {
619166255Sdelphij		if (z.avail_out == 0) {
620166255Sdelphij			if (write(out, outbufp, BUFLEN) != BUFLEN) {
621166255Sdelphij				maybe_warn("write");
622166255Sdelphij				out_tot = -1;
623166255Sdelphij				goto out;
624166255Sdelphij			}
625166255Sdelphij
626166255Sdelphij			out_tot += BUFLEN;
627166255Sdelphij			z.next_out = (unsigned char *)outbufp;
628166255Sdelphij			z.avail_out = BUFLEN;
629166255Sdelphij		}
630166255Sdelphij
631166255Sdelphij		if (z.avail_in == 0) {
632166255Sdelphij			in_size = read(in, inbufp, BUFLEN);
633166255Sdelphij			if (in_size < 0) {
634166255Sdelphij				maybe_warn("read");
635166255Sdelphij				in_tot = -1;
636166255Sdelphij				goto out;
637166255Sdelphij			}
638166255Sdelphij			if (in_size == 0)
639166255Sdelphij				break;
640166255Sdelphij
641166255Sdelphij			crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
642166255Sdelphij			in_tot += in_size;
643166255Sdelphij			z.next_in = (unsigned char *)inbufp;
644166255Sdelphij			z.avail_in = in_size;
645166255Sdelphij		}
646166255Sdelphij
647166255Sdelphij		error = deflate(&z, Z_NO_FLUSH);
648166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
649166255Sdelphij			maybe_warnx("deflate failed");
650166255Sdelphij			in_tot = -1;
651166255Sdelphij			goto out;
652166255Sdelphij		}
653166255Sdelphij	}
654166255Sdelphij
655166255Sdelphij	/* clean up */
656166255Sdelphij	for (;;) {
657166255Sdelphij		size_t len;
658166255Sdelphij		ssize_t w;
659166255Sdelphij
660166255Sdelphij		error = deflate(&z, Z_FINISH);
661166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
662166255Sdelphij			maybe_warnx("deflate failed");
663166255Sdelphij			in_tot = -1;
664166255Sdelphij			goto out;
665166255Sdelphij		}
666166255Sdelphij
667166255Sdelphij		len = (char *)z.next_out - outbufp;
668166255Sdelphij
669166255Sdelphij		w = write(out, outbufp, len);
670166255Sdelphij		if (w == -1 || (size_t)w != len) {
671166255Sdelphij			maybe_warn("write");
672166255Sdelphij			out_tot = -1;
673166255Sdelphij			goto out;
674166255Sdelphij		}
675166255Sdelphij		out_tot += len;
676166255Sdelphij		z.next_out = (unsigned char *)outbufp;
677166255Sdelphij		z.avail_out = BUFLEN;
678166255Sdelphij
679166255Sdelphij		if (error == Z_STREAM_END)
680166255Sdelphij			break;
681166255Sdelphij	}
682166255Sdelphij
683166255Sdelphij	if (deflateEnd(&z) != Z_OK) {
684166255Sdelphij		maybe_warnx("deflateEnd failed");
685166255Sdelphij		in_tot = -1;
686166255Sdelphij		goto out;
687166255Sdelphij	}
688166255Sdelphij
689166255Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
690166255Sdelphij		 (int)crc & 0xff,
691166255Sdelphij		 (int)(crc >> 8) & 0xff,
692166255Sdelphij		 (int)(crc >> 16) & 0xff,
693166255Sdelphij		 (int)(crc >> 24) & 0xff,
694166255Sdelphij		 (int)in_tot & 0xff,
695166255Sdelphij		 (int)(in_tot >> 8) & 0xff,
696166255Sdelphij		 (int)(in_tot >> 16) & 0xff,
697166255Sdelphij		 (int)(in_tot >> 24) & 0xff);
698166255Sdelphij	if (i != 8)
699166255Sdelphij		maybe_err("snprintf");
700166255Sdelphij	if (write(out, outbufp, i) != i) {
701166255Sdelphij		maybe_warn("write");
702166255Sdelphij		in_tot = -1;
703166255Sdelphij	} else
704166255Sdelphij		out_tot += i;
705166255Sdelphij
706166255Sdelphijout:
707166255Sdelphij	if (inbufp != NULL)
708166255Sdelphij		free(inbufp);
709166255Sdelphij	if (outbufp != NULL)
710166255Sdelphij		free(outbufp);
711166255Sdelphij	if (gsizep)
712166255Sdelphij		*gsizep = out_tot;
713166255Sdelphij	return in_tot;
714166255Sdelphij}
715166255Sdelphij
716166255Sdelphij/*
717166255Sdelphij * uncompress input to output then close the input.  return the
718166255Sdelphij * uncompressed size written, and put the compressed sized read
719166255Sdelphij * into `*gsizep'.
720166255Sdelphij */
721166255Sdelphijstatic off_t
722166255Sdelphijgz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
723166255Sdelphij	      const char *filename)
724166255Sdelphij{
725166255Sdelphij	z_stream z;
726166255Sdelphij	char *outbufp, *inbufp;
727166255Sdelphij	off_t out_tot = -1, in_tot = 0;
728166255Sdelphij	uint32_t out_sub_tot = 0;
729166255Sdelphij	enum {
730166255Sdelphij		GZSTATE_MAGIC0,
731166255Sdelphij		GZSTATE_MAGIC1,
732166255Sdelphij		GZSTATE_METHOD,
733166255Sdelphij		GZSTATE_FLAGS,
734166255Sdelphij		GZSTATE_SKIPPING,
735166255Sdelphij		GZSTATE_EXTRA,
736166255Sdelphij		GZSTATE_EXTRA2,
737166255Sdelphij		GZSTATE_EXTRA3,
738166255Sdelphij		GZSTATE_ORIGNAME,
739166255Sdelphij		GZSTATE_COMMENT,
740166255Sdelphij		GZSTATE_HEAD_CRC1,
741166255Sdelphij		GZSTATE_HEAD_CRC2,
742166255Sdelphij		GZSTATE_INIT,
743166255Sdelphij		GZSTATE_READ,
744166255Sdelphij		GZSTATE_CRC,
745166255Sdelphij		GZSTATE_LEN,
746166255Sdelphij	} state = GZSTATE_MAGIC0;
747166255Sdelphij	int flags = 0, skip_count = 0;
748166255Sdelphij	int error = Z_STREAM_ERROR, done_reading = 0;
749166255Sdelphij	uLong crc = 0;
750166255Sdelphij	ssize_t wr;
751166255Sdelphij	int needmore = 0;
752166255Sdelphij
753166255Sdelphij#define ADVANCE()       { z.next_in++; z.avail_in--; }
754166255Sdelphij
755166255Sdelphij	if ((outbufp = malloc(BUFLEN)) == NULL) {
756166255Sdelphij		maybe_err("malloc failed");
757166255Sdelphij		goto out2;
758166255Sdelphij	}
759166255Sdelphij	if ((inbufp = malloc(BUFLEN)) == NULL) {
760166255Sdelphij		maybe_err("malloc failed");
761166255Sdelphij		goto out1;
762166255Sdelphij	}
763166255Sdelphij
764166255Sdelphij	memset(&z, 0, sizeof z);
765166255Sdelphij	z.avail_in = prelen;
766166255Sdelphij	z.next_in = (unsigned char *)pre;
767166255Sdelphij	z.avail_out = BUFLEN;
768166255Sdelphij	z.next_out = (unsigned char *)outbufp;
769166255Sdelphij	z.zalloc = NULL;
770166255Sdelphij	z.zfree = NULL;
771166255Sdelphij	z.opaque = 0;
772166255Sdelphij
773166255Sdelphij	in_tot = prelen;
774166255Sdelphij	out_tot = 0;
775166255Sdelphij
776166255Sdelphij	for (;;) {
777166255Sdelphij		if ((z.avail_in == 0 || needmore) && done_reading == 0) {
778166255Sdelphij			ssize_t in_size;
779166255Sdelphij
780166255Sdelphij			if (z.avail_in > 0) {
781166255Sdelphij				memmove(inbufp, z.next_in, z.avail_in);
782166255Sdelphij			}
783166255Sdelphij			z.next_in = (unsigned char *)inbufp;
784166255Sdelphij			in_size = read(in, z.next_in + z.avail_in,
785166255Sdelphij			    BUFLEN - z.avail_in);
786166255Sdelphij
787166255Sdelphij			if (in_size == -1) {
788166255Sdelphij				maybe_warn("failed to read stdin");
789166255Sdelphij				goto stop_and_fail;
790166255Sdelphij			} else if (in_size == 0) {
791166255Sdelphij				done_reading = 1;
792166255Sdelphij			}
793166255Sdelphij
794166255Sdelphij			z.avail_in += in_size;
795166255Sdelphij			needmore = 0;
796166255Sdelphij
797166255Sdelphij			in_tot += in_size;
798166255Sdelphij		}
799166255Sdelphij		if (z.avail_in == 0) {
800166255Sdelphij			if (done_reading && state != GZSTATE_MAGIC0) {
801166255Sdelphij				maybe_warnx("%s: unexpected end of file",
802166255Sdelphij					    filename);
803166255Sdelphij				goto stop_and_fail;
804166255Sdelphij			}
805166255Sdelphij			goto stop;
806166255Sdelphij		}
807166255Sdelphij		switch (state) {
808166255Sdelphij		case GZSTATE_MAGIC0:
809166255Sdelphij			if (*z.next_in != GZIP_MAGIC0) {
810166255Sdelphij				if (in_tot > 0) {
811166255Sdelphij					maybe_warnx("%s: trailing garbage "
812166255Sdelphij						    "ignored", filename);
813166255Sdelphij					goto stop;
814166255Sdelphij				}
815166255Sdelphij				maybe_warnx("input not gziped (MAGIC0)");
816166255Sdelphij				goto stop_and_fail;
817166255Sdelphij			}
818166255Sdelphij			ADVANCE();
819166255Sdelphij			state++;
820166255Sdelphij			out_sub_tot = 0;
821166255Sdelphij			crc = crc32(0L, Z_NULL, 0);
822166255Sdelphij			break;
823166255Sdelphij
824166255Sdelphij		case GZSTATE_MAGIC1:
825166255Sdelphij			if (*z.next_in != GZIP_MAGIC1 &&
826166255Sdelphij			    *z.next_in != GZIP_OMAGIC1) {
827166255Sdelphij				maybe_warnx("input not gziped (MAGIC1)");
828166255Sdelphij				goto stop_and_fail;
829166255Sdelphij			}
830166255Sdelphij			ADVANCE();
831166255Sdelphij			state++;
832166255Sdelphij			break;
833166255Sdelphij
834166255Sdelphij		case GZSTATE_METHOD:
835166255Sdelphij			if (*z.next_in != Z_DEFLATED) {
836166255Sdelphij				maybe_warnx("unknown compression method");
837166255Sdelphij				goto stop_and_fail;
838166255Sdelphij			}
839166255Sdelphij			ADVANCE();
840166255Sdelphij			state++;
841166255Sdelphij			break;
842166255Sdelphij
843166255Sdelphij		case GZSTATE_FLAGS:
844166255Sdelphij			flags = *z.next_in;
845166255Sdelphij			ADVANCE();
846166255Sdelphij			skip_count = 6;
847166255Sdelphij			state++;
848166255Sdelphij			break;
849166255Sdelphij
850166255Sdelphij		case GZSTATE_SKIPPING:
851166255Sdelphij			if (skip_count > 0) {
852166255Sdelphij				skip_count--;
853166255Sdelphij				ADVANCE();
854166255Sdelphij			} else
855166255Sdelphij				state++;
856166255Sdelphij			break;
857166255Sdelphij
858166255Sdelphij		case GZSTATE_EXTRA:
859166255Sdelphij			if ((flags & EXTRA_FIELD) == 0) {
860166255Sdelphij				state = GZSTATE_ORIGNAME;
861166255Sdelphij				break;
862166255Sdelphij			}
863166255Sdelphij			skip_count = *z.next_in;
864166255Sdelphij			ADVANCE();
865166255Sdelphij			state++;
866166255Sdelphij			break;
867166255Sdelphij
868166255Sdelphij		case GZSTATE_EXTRA2:
869166255Sdelphij			skip_count |= ((*z.next_in) << 8);
870166255Sdelphij			ADVANCE();
871166255Sdelphij			state++;
872166255Sdelphij			break;
873166255Sdelphij
874166255Sdelphij		case GZSTATE_EXTRA3:
875166255Sdelphij			if (skip_count > 0) {
876166255Sdelphij				skip_count--;
877166255Sdelphij				ADVANCE();
878166255Sdelphij			} else
879166255Sdelphij				state++;
880166255Sdelphij			break;
881166255Sdelphij
882166255Sdelphij		case GZSTATE_ORIGNAME:
883166255Sdelphij			if ((flags & ORIG_NAME) == 0) {
884166255Sdelphij				state++;
885166255Sdelphij				break;
886166255Sdelphij			}
887166255Sdelphij			if (*z.next_in == 0)
888166255Sdelphij				state++;
889166255Sdelphij			ADVANCE();
890166255Sdelphij			break;
891166255Sdelphij
892166255Sdelphij		case GZSTATE_COMMENT:
893166255Sdelphij			if ((flags & COMMENT) == 0) {
894166255Sdelphij				state++;
895166255Sdelphij				break;
896166255Sdelphij			}
897166255Sdelphij			if (*z.next_in == 0)
898166255Sdelphij				state++;
899166255Sdelphij			ADVANCE();
900166255Sdelphij			break;
901166255Sdelphij
902166255Sdelphij		case GZSTATE_HEAD_CRC1:
903166255Sdelphij			if (flags & HEAD_CRC)
904166255Sdelphij				skip_count = 2;
905166255Sdelphij			else
906166255Sdelphij				skip_count = 0;
907166255Sdelphij			state++;
908166255Sdelphij			break;
909166255Sdelphij
910166255Sdelphij		case GZSTATE_HEAD_CRC2:
911166255Sdelphij			if (skip_count > 0) {
912166255Sdelphij				skip_count--;
913166255Sdelphij				ADVANCE();
914166255Sdelphij			} else
915166255Sdelphij				state++;
916166255Sdelphij			break;
917166255Sdelphij
918166255Sdelphij		case GZSTATE_INIT:
919166255Sdelphij			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
920166255Sdelphij				maybe_warnx("failed to inflateInit");
921166255Sdelphij				goto stop_and_fail;
922166255Sdelphij			}
923166255Sdelphij			state++;
924166255Sdelphij			break;
925166255Sdelphij
926166255Sdelphij		case GZSTATE_READ:
927166255Sdelphij			error = inflate(&z, Z_FINISH);
928166255Sdelphij			switch (error) {
929166255Sdelphij			/* Z_BUF_ERROR goes with Z_FINISH... */
930166255Sdelphij			case Z_BUF_ERROR:
931213044Sdelphij				if (z.avail_out > 0 && !done_reading)
932213044Sdelphij					continue;
933222210Sdelphij
934166255Sdelphij			case Z_STREAM_END:
935166255Sdelphij			case Z_OK:
936166255Sdelphij				break;
937166255Sdelphij
938166255Sdelphij			case Z_NEED_DICT:
939166255Sdelphij				maybe_warnx("Z_NEED_DICT error");
940166255Sdelphij				goto stop_and_fail;
941166255Sdelphij			case Z_DATA_ERROR:
942166255Sdelphij				maybe_warnx("data stream error");
943166255Sdelphij				goto stop_and_fail;
944166255Sdelphij			case Z_STREAM_ERROR:
945166255Sdelphij				maybe_warnx("internal stream error");
946166255Sdelphij				goto stop_and_fail;
947166255Sdelphij			case Z_MEM_ERROR:
948166255Sdelphij				maybe_warnx("memory allocation error");
949166255Sdelphij				goto stop_and_fail;
950166255Sdelphij
951166255Sdelphij			default:
952166255Sdelphij				maybe_warn("unknown error from inflate(): %d",
953166255Sdelphij				    error);
954166255Sdelphij			}
955166255Sdelphij			wr = BUFLEN - z.avail_out;
956166255Sdelphij
957166255Sdelphij			if (wr != 0) {
958166255Sdelphij				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
959166255Sdelphij				if (
960166255Sdelphij#ifndef SMALL
961166255Sdelphij				    /* don't write anything with -t */
962166255Sdelphij				    tflag == 0 &&
963166255Sdelphij#endif
964166255Sdelphij				    write(out, outbufp, wr) != wr) {
965166255Sdelphij					maybe_warn("error writing to output");
966166255Sdelphij					goto stop_and_fail;
967166255Sdelphij				}
968166255Sdelphij
969166255Sdelphij				out_tot += wr;
970166255Sdelphij				out_sub_tot += wr;
971166255Sdelphij			}
972166255Sdelphij
973166255Sdelphij			if (error == Z_STREAM_END) {
974166255Sdelphij				inflateEnd(&z);
975166255Sdelphij				state++;
976166255Sdelphij			}
977166255Sdelphij
978166255Sdelphij			z.next_out = (unsigned char *)outbufp;
979166255Sdelphij			z.avail_out = BUFLEN;
980166255Sdelphij
981166255Sdelphij			break;
982166255Sdelphij		case GZSTATE_CRC:
983166255Sdelphij			{
984166255Sdelphij				uLong origcrc;
985166255Sdelphij
986166255Sdelphij				if (z.avail_in < 4) {
987166255Sdelphij					if (!done_reading) {
988166255Sdelphij						needmore = 1;
989166255Sdelphij						continue;
990166255Sdelphij					}
991166255Sdelphij					maybe_warnx("truncated input");
992166255Sdelphij					goto stop_and_fail;
993166255Sdelphij				}
994166255Sdelphij				origcrc = ((unsigned)z.next_in[0] & 0xff) |
995166255Sdelphij					((unsigned)z.next_in[1] & 0xff) << 8 |
996166255Sdelphij					((unsigned)z.next_in[2] & 0xff) << 16 |
997166255Sdelphij					((unsigned)z.next_in[3] & 0xff) << 24;
998166255Sdelphij				if (origcrc != crc) {
999166255Sdelphij					maybe_warnx("invalid compressed"
1000166255Sdelphij					     " data--crc error");
1001166255Sdelphij					goto stop_and_fail;
1002166255Sdelphij				}
1003166255Sdelphij			}
1004166255Sdelphij
1005166255Sdelphij			z.avail_in -= 4;
1006166255Sdelphij			z.next_in += 4;
1007166255Sdelphij
1008166255Sdelphij			if (!z.avail_in && done_reading) {
1009166255Sdelphij				goto stop;
1010166255Sdelphij			}
1011166255Sdelphij			state++;
1012166255Sdelphij			break;
1013166255Sdelphij		case GZSTATE_LEN:
1014166255Sdelphij			{
1015166255Sdelphij				uLong origlen;
1016166255Sdelphij
1017166255Sdelphij				if (z.avail_in < 4) {
1018166255Sdelphij					if (!done_reading) {
1019166255Sdelphij						needmore = 1;
1020166255Sdelphij						continue;
1021166255Sdelphij					}
1022166255Sdelphij					maybe_warnx("truncated input");
1023166255Sdelphij					goto stop_and_fail;
1024166255Sdelphij				}
1025166255Sdelphij				origlen = ((unsigned)z.next_in[0] & 0xff) |
1026166255Sdelphij					((unsigned)z.next_in[1] & 0xff) << 8 |
1027166255Sdelphij					((unsigned)z.next_in[2] & 0xff) << 16 |
1028166255Sdelphij					((unsigned)z.next_in[3] & 0xff) << 24;
1029166255Sdelphij
1030166255Sdelphij				if (origlen != out_sub_tot) {
1031166255Sdelphij					maybe_warnx("invalid compressed"
1032166255Sdelphij					     " data--length error");
1033166255Sdelphij					goto stop_and_fail;
1034166255Sdelphij				}
1035166255Sdelphij			}
1036166255Sdelphij
1037166255Sdelphij			z.avail_in -= 4;
1038166255Sdelphij			z.next_in += 4;
1039166255Sdelphij
1040166255Sdelphij			if (error < 0) {
1041166255Sdelphij				maybe_warnx("decompression error");
1042166255Sdelphij				goto stop_and_fail;
1043166255Sdelphij			}
1044166255Sdelphij			state = GZSTATE_MAGIC0;
1045166255Sdelphij			break;
1046166255Sdelphij		}
1047166255Sdelphij		continue;
1048166255Sdelphijstop_and_fail:
1049166255Sdelphij		out_tot = -1;
1050166255Sdelphijstop:
1051166255Sdelphij		break;
1052166255Sdelphij	}
1053166255Sdelphij	if (state > GZSTATE_INIT)
1054166255Sdelphij		inflateEnd(&z);
1055166255Sdelphij
1056166255Sdelphij	free(inbufp);
1057166255Sdelphijout1:
1058166255Sdelphij	free(outbufp);
1059166255Sdelphijout2:
1060166255Sdelphij	if (gsizep)
1061166255Sdelphij		*gsizep = in_tot;
1062166255Sdelphij	return (out_tot);
1063166255Sdelphij}
1064166255Sdelphij
1065166255Sdelphij#ifndef SMALL
1066166255Sdelphij/*
1067166255Sdelphij * set the owner, mode, flags & utimes using the given file descriptor.
1068166255Sdelphij * file is only used in possible warning messages.
1069166255Sdelphij */
1070166255Sdelphijstatic void
1071166255Sdelphijcopymodes(int fd, const struct stat *sbp, const char *file)
1072166255Sdelphij{
1073166255Sdelphij	struct timeval times[2];
1074166255Sdelphij	struct stat sb;
1075166255Sdelphij
1076166255Sdelphij	/*
1077166255Sdelphij	 * If we have no info on the input, give this file some
1078166255Sdelphij	 * default values and return..
1079166255Sdelphij	 */
1080166255Sdelphij	if (sbp == NULL) {
1081166255Sdelphij		mode_t mask = umask(022);
1082166255Sdelphij
1083166255Sdelphij		(void)fchmod(fd, DEFFILEMODE & ~mask);
1084166255Sdelphij		(void)umask(mask);
1085166255Sdelphij		return;
1086166255Sdelphij	}
1087166255Sdelphij	sb = *sbp;
1088166255Sdelphij
1089166255Sdelphij	/* if the chown fails, remove set-id bits as-per compress(1) */
1090166255Sdelphij	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1091166255Sdelphij		if (errno != EPERM)
1092166255Sdelphij			maybe_warn("couldn't fchown: %s", file);
1093166255Sdelphij		sb.st_mode &= ~(S_ISUID|S_ISGID);
1094166255Sdelphij	}
1095166255Sdelphij
1096166255Sdelphij	/* we only allow set-id and the 9 normal permission bits */
1097166255Sdelphij	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1098166255Sdelphij	if (fchmod(fd, sb.st_mode) < 0)
1099166255Sdelphij		maybe_warn("couldn't fchmod: %s", file);
1100166255Sdelphij
1101205793Sed	TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atim);
1102205793Sed	TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtim);
1103166255Sdelphij	if (futimes(fd, times) < 0)
1104166255Sdelphij		maybe_warn("couldn't utimes: %s", file);
1105176970Srwatson
1106176970Srwatson	/* only try flags if they exist already */
1107176970Srwatson        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1108176970Srwatson		maybe_warn("couldn't fchflags: %s", file);
1109166255Sdelphij}
1110166255Sdelphij#endif
1111166255Sdelphij
1112166255Sdelphij/* what sort of file is this? */
1113166255Sdelphijstatic enum filetype
1114166255Sdelphijfile_gettype(u_char *buf)
1115166255Sdelphij{
1116166255Sdelphij
1117166255Sdelphij	if (buf[0] == GZIP_MAGIC0 &&
1118166255Sdelphij	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1119166255Sdelphij		return FT_GZIP;
1120166255Sdelphij	else
1121166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1122166255Sdelphij	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1123166255Sdelphij	    buf[3] >= '0' && buf[3] <= '9')
1124166255Sdelphij		return FT_BZIP2;
1125166255Sdelphij	else
1126166255Sdelphij#endif
1127166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1128166255Sdelphij	if (memcmp(buf, Z_MAGIC, 2) == 0)
1129166255Sdelphij		return FT_Z;
1130166255Sdelphij	else
1131166255Sdelphij#endif
1132194579Sdelphij#ifndef NO_PACK_SUPPORT
1133194579Sdelphij	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1134194579Sdelphij		return FT_PACK;
1135194579Sdelphij	else
1136194579Sdelphij#endif
1137226184Sdelphij#ifndef NO_XZ_SUPPORT
1138226184Sdelphij	if (memcmp(buf, XZ_MAGIC, 4) == 0)	/* XXX: We only have 4 bytes */
1139226184Sdelphij		return FT_XZ;
1140226184Sdelphij	else
1141226184Sdelphij#endif
1142166255Sdelphij		return FT_UNKNOWN;
1143166255Sdelphij}
1144166255Sdelphij
1145166255Sdelphij#ifndef SMALL
1146166255Sdelphij/* check the outfile is OK. */
1147166255Sdelphijstatic int
1148166255Sdelphijcheck_outfile(const char *outfile)
1149166255Sdelphij{
1150166255Sdelphij	struct stat sb;
1151166255Sdelphij	int ok = 1;
1152166255Sdelphij
1153166255Sdelphij	if (lflag == 0 && stat(outfile, &sb) == 0) {
1154166255Sdelphij		if (fflag)
1155166255Sdelphij			unlink(outfile);
1156166255Sdelphij		else if (isatty(STDIN_FILENO)) {
1157166255Sdelphij			char ans[10] = { 'n', '\0' };	/* default */
1158166255Sdelphij
1159166255Sdelphij			fprintf(stderr, "%s already exists -- do you wish to "
1160166255Sdelphij					"overwrite (y or n)? " , outfile);
1161166255Sdelphij			(void)fgets(ans, sizeof(ans) - 1, stdin);
1162166255Sdelphij			if (ans[0] != 'y' && ans[0] != 'Y') {
1163166363Sdelphij				fprintf(stderr, "\tnot overwriting\n");
1164166255Sdelphij				ok = 0;
1165166255Sdelphij			} else
1166166255Sdelphij				unlink(outfile);
1167166255Sdelphij		} else {
1168166255Sdelphij			maybe_warnx("%s already exists -- skipping", outfile);
1169166255Sdelphij			ok = 0;
1170166255Sdelphij		}
1171166255Sdelphij	}
1172166255Sdelphij	return ok;
1173166255Sdelphij}
1174166255Sdelphij
1175166255Sdelphijstatic void
1176166255Sdelphijunlink_input(const char *file, const struct stat *sb)
1177166255Sdelphij{
1178166255Sdelphij	struct stat nsb;
1179166255Sdelphij
1180170053Sdelphij	if (kflag)
1181170053Sdelphij		return;
1182166255Sdelphij	if (stat(file, &nsb) != 0)
1183213927Sbcr		/* Must be gone already */
1184166255Sdelphij		return;
1185166255Sdelphij	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1186166255Sdelphij		/* Definitely a different file */
1187166255Sdelphij		return;
1188166255Sdelphij	unlink(file);
1189166255Sdelphij}
1190207247Sdelphij
1191207247Sdelphijstatic void
1192207247Sdelphijsigint_handler(int signo __unused)
1193207247Sdelphij{
1194207247Sdelphij
1195207247Sdelphij	if (remove_file != NULL)
1196207247Sdelphij		unlink(remove_file);
1197207284Sdelphij	_exit(2);
1198207247Sdelphij}
1199166255Sdelphij#endif
1200166255Sdelphij
1201166255Sdelphijstatic const suffixes_t *
1202166255Sdelphijcheck_suffix(char *file, int xlate)
1203166255Sdelphij{
1204166255Sdelphij	const suffixes_t *s;
1205166255Sdelphij	int len = strlen(file);
1206166255Sdelphij	char *sp;
1207166255Sdelphij
1208166255Sdelphij	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1209166255Sdelphij		/* if it doesn't fit in "a.suf", don't bother */
1210166255Sdelphij		if (s->ziplen >= len)
1211166255Sdelphij			continue;
1212166255Sdelphij		sp = file + len - s->ziplen;
1213166255Sdelphij		if (strcmp(s->zipped, sp) != 0)
1214166255Sdelphij			continue;
1215166255Sdelphij		if (xlate)
1216166255Sdelphij			strcpy(sp, s->normal);
1217166255Sdelphij		return s;
1218166255Sdelphij	}
1219166255Sdelphij	return NULL;
1220166255Sdelphij}
1221166255Sdelphij
1222166255Sdelphij/*
1223166255Sdelphij * compress the given file: create a corresponding .gz file and remove the
1224166255Sdelphij * original.
1225166255Sdelphij */
1226166255Sdelphijstatic off_t
1227166255Sdelphijfile_compress(char *file, char *outfile, size_t outsize)
1228166255Sdelphij{
1229166255Sdelphij	int in;
1230166255Sdelphij	int out;
1231166255Sdelphij	off_t size, insize;
1232166255Sdelphij#ifndef SMALL
1233166255Sdelphij	struct stat isb, osb;
1234166255Sdelphij	const suffixes_t *suff;
1235166255Sdelphij#endif
1236166255Sdelphij
1237166255Sdelphij	in = open(file, O_RDONLY);
1238166255Sdelphij	if (in == -1) {
1239166255Sdelphij		maybe_warn("can't open %s", file);
1240209017Sdelphij		return (-1);
1241166255Sdelphij	}
1242166255Sdelphij
1243208888Sdelphij#ifndef SMALL
1244208888Sdelphij	if (fstat(in, &isb) != 0) {
1245208888Sdelphij		maybe_warn("couldn't stat: %s", file);
1246208888Sdelphij		close(in);
1247209017Sdelphij		return (-1);
1248208888Sdelphij	}
1249208888Sdelphij#endif
1250208888Sdelphij
1251166255Sdelphij	if (cflag == 0) {
1252166255Sdelphij#ifndef SMALL
1253209017Sdelphij		if (isb.st_nlink > 1 && fflag == 0) {
1254209017Sdelphij			maybe_warnx("%s has %d other link%s -- skipping",
1255209017Sdelphij			    file, isb.st_nlink - 1,
1256209017Sdelphij			    (isb.st_nlink - 1) == 1 ? "" : "s");
1257208889Sdelphij			close(in);
1258209017Sdelphij			return (-1);
1259208889Sdelphij		}
1260166255Sdelphij
1261209017Sdelphij		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1262209017Sdelphij		    suff->zipped[0] != 0) {
1263166255Sdelphij			maybe_warnx("%s already has %s suffix -- unchanged",
1264209017Sdelphij			    file, suff->zipped);
1265166255Sdelphij			close(in);
1266209017Sdelphij			return (-1);
1267166255Sdelphij		}
1268166255Sdelphij#endif
1269166255Sdelphij
1270166255Sdelphij		/* Add (usually) .gz to filename */
1271166255Sdelphij		if ((size_t)snprintf(outfile, outsize, "%s%s",
1272209017Sdelphij		    file, suffixes[0].zipped) >= outsize)
1273195988Sdelphij			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1274209017Sdelphij			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1275166255Sdelphij
1276166255Sdelphij#ifndef SMALL
1277166255Sdelphij		if (check_outfile(outfile) == 0) {
1278166255Sdelphij			close(in);
1279209017Sdelphij			return (-1);
1280166255Sdelphij		}
1281166255Sdelphij#endif
1282166255Sdelphij	}
1283166255Sdelphij
1284166255Sdelphij	if (cflag == 0) {
1285166255Sdelphij		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1286166255Sdelphij		if (out == -1) {
1287166255Sdelphij			maybe_warn("could not create output: %s", outfile);
1288166255Sdelphij			fclose(stdin);
1289209017Sdelphij			return (-1);
1290166255Sdelphij		}
1291207247Sdelphij#ifndef SMALL
1292207247Sdelphij		remove_file = outfile;
1293207247Sdelphij#endif
1294166255Sdelphij	} else
1295166255Sdelphij		out = STDOUT_FILENO;
1296166255Sdelphij
1297166255Sdelphij	insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1298166255Sdelphij
1299166255Sdelphij	(void)close(in);
1300166255Sdelphij
1301166255Sdelphij	/*
1302166255Sdelphij	 * If there was an error, insize will be -1.
1303166255Sdelphij	 * If we compressed to stdout, just return the size.
1304166255Sdelphij	 * Otherwise stat the file and check it is the correct size.
1305166255Sdelphij	 * We only blow away the file if we can stat the output and it
1306166255Sdelphij	 * has the expected size.
1307166255Sdelphij	 */
1308166255Sdelphij	if (cflag != 0)
1309209017Sdelphij		return (insize == -1 ? -1 : size);
1310166255Sdelphij
1311166255Sdelphij#ifndef SMALL
1312166255Sdelphij	if (fstat(out, &osb) != 0) {
1313166255Sdelphij		maybe_warn("couldn't stat: %s", outfile);
1314166255Sdelphij		goto bad_outfile;
1315166255Sdelphij	}
1316166255Sdelphij
1317166255Sdelphij	if (osb.st_size != size) {
1318209017Sdelphij		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1319209017Sdelphij		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1320166255Sdelphij		goto bad_outfile;
1321166255Sdelphij	}
1322166255Sdelphij
1323166255Sdelphij	copymodes(out, &isb, outfile);
1324207247Sdelphij	remove_file = NULL;
1325166255Sdelphij#endif
1326166255Sdelphij	if (close(out) == -1)
1327166255Sdelphij		maybe_warn("couldn't close output");
1328166255Sdelphij
1329166255Sdelphij	/* output is good, ok to delete input */
1330166255Sdelphij	unlink_input(file, &isb);
1331209017Sdelphij	return (size);
1332166255Sdelphij
1333166255Sdelphij#ifndef SMALL
1334166255Sdelphij    bad_outfile:
1335166255Sdelphij	if (close(out) == -1)
1336166255Sdelphij		maybe_warn("couldn't close output");
1337166255Sdelphij
1338166255Sdelphij	maybe_warnx("leaving original %s", file);
1339166255Sdelphij	unlink(outfile);
1340209017Sdelphij	return (size);
1341166255Sdelphij#endif
1342166255Sdelphij}
1343166255Sdelphij
1344166255Sdelphij/* uncompress the given file and remove the original */
1345166255Sdelphijstatic off_t
1346166255Sdelphijfile_uncompress(char *file, char *outfile, size_t outsize)
1347166255Sdelphij{
1348166255Sdelphij	struct stat isb, osb;
1349166255Sdelphij	off_t size;
1350166255Sdelphij	ssize_t rbytes;
1351166255Sdelphij	unsigned char header1[4];
1352166255Sdelphij	enum filetype method;
1353194508Sdelphij	int fd, ofd, zfd = -1;
1354166255Sdelphij#ifndef SMALL
1355206387Sdelphij	ssize_t rv;
1356166255Sdelphij	time_t timestamp = 0;
1357166255Sdelphij	unsigned char name[PATH_MAX + 1];
1358166255Sdelphij#endif
1359166255Sdelphij
1360166255Sdelphij	/* gather the old name info */
1361166255Sdelphij
1362166255Sdelphij	fd = open(file, O_RDONLY);
1363166255Sdelphij	if (fd < 0) {
1364166255Sdelphij		maybe_warn("can't open %s", file);
1365166255Sdelphij		goto lose;
1366166255Sdelphij	}
1367166255Sdelphij
1368166255Sdelphij	strlcpy(outfile, file, outsize);
1369166255Sdelphij	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1370166255Sdelphij		maybe_warnx("%s: unknown suffix -- ignored", file);
1371166255Sdelphij		goto lose;
1372166255Sdelphij	}
1373166255Sdelphij
1374166255Sdelphij	rbytes = read(fd, header1, sizeof header1);
1375166255Sdelphij	if (rbytes != sizeof header1) {
1376166255Sdelphij		/* we don't want to fail here. */
1377166255Sdelphij#ifndef SMALL
1378166255Sdelphij		if (fflag)
1379166255Sdelphij			goto lose;
1380166255Sdelphij#endif
1381166255Sdelphij		if (rbytes == -1)
1382166255Sdelphij			maybe_warn("can't read %s", file);
1383166255Sdelphij		else
1384166255Sdelphij			goto unexpected_EOF;
1385166255Sdelphij		goto lose;
1386166255Sdelphij	}
1387166255Sdelphij
1388166255Sdelphij	method = file_gettype(header1);
1389166255Sdelphij#ifndef SMALL
1390166255Sdelphij	if (fflag == 0 && method == FT_UNKNOWN) {
1391166255Sdelphij		maybe_warnx("%s: not in gzip format", file);
1392166255Sdelphij		goto lose;
1393166255Sdelphij	}
1394166255Sdelphij
1395166255Sdelphij#endif
1396166255Sdelphij
1397166255Sdelphij#ifndef SMALL
1398166255Sdelphij	if (method == FT_GZIP && Nflag) {
1399166255Sdelphij		unsigned char ts[4];	/* timestamp */
1400166255Sdelphij
1401166255Sdelphij		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1402194916Sdelphij		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1403166255Sdelphij			goto unexpected_EOF;
1404166255Sdelphij		if (rv == -1) {
1405166255Sdelphij			if (!fflag)
1406166255Sdelphij				maybe_warn("can't read %s", file);
1407166255Sdelphij			goto lose;
1408166255Sdelphij		}
1409166255Sdelphij		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1410166255Sdelphij
1411166255Sdelphij		if (header1[3] & ORIG_NAME) {
1412166255Sdelphij			rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1413166255Sdelphij			if (rbytes < 0) {
1414166255Sdelphij				maybe_warn("can't read %s", file);
1415166255Sdelphij				goto lose;
1416166255Sdelphij			}
1417166255Sdelphij			if (name[0] != 0) {
1418166255Sdelphij				/* preserve original directory name */
1419166255Sdelphij				char *dp = strrchr(file, '/');
1420166255Sdelphij				if (dp == NULL)
1421166255Sdelphij					dp = file;
1422166255Sdelphij				else
1423166255Sdelphij					dp++;
1424166255Sdelphij				snprintf(outfile, outsize, "%.*s%.*s",
1425166255Sdelphij						(int) (dp - file),
1426166255Sdelphij						file, (int) rbytes, name);
1427166255Sdelphij			}
1428166255Sdelphij		}
1429166255Sdelphij	}
1430166255Sdelphij#endif
1431166255Sdelphij	lseek(fd, 0, SEEK_SET);
1432166255Sdelphij
1433166255Sdelphij	if (cflag == 0 || lflag) {
1434166255Sdelphij		if (fstat(fd, &isb) != 0)
1435166255Sdelphij			goto lose;
1436166255Sdelphij#ifndef SMALL
1437166255Sdelphij		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1438166255Sdelphij			maybe_warnx("%s has %d other links -- skipping",
1439166255Sdelphij			    file, isb.st_nlink - 1);
1440166255Sdelphij			goto lose;
1441166255Sdelphij		}
1442166255Sdelphij		if (nflag == 0 && timestamp)
1443166255Sdelphij			isb.st_mtime = timestamp;
1444166255Sdelphij		if (check_outfile(outfile) == 0)
1445166255Sdelphij			goto lose;
1446166255Sdelphij#endif
1447166255Sdelphij	}
1448166255Sdelphij
1449166255Sdelphij	if (cflag == 0 && lflag == 0) {
1450166255Sdelphij		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1451166255Sdelphij		if (zfd == STDOUT_FILENO) {
1452166255Sdelphij			/* We won't close STDOUT_FILENO later... */
1453166255Sdelphij			zfd = dup(zfd);
1454166255Sdelphij			close(STDOUT_FILENO);
1455166255Sdelphij		}
1456166255Sdelphij		if (zfd == -1) {
1457166255Sdelphij			maybe_warn("can't open %s", outfile);
1458166255Sdelphij			goto lose;
1459166255Sdelphij		}
1460207247Sdelphij#ifndef SMALL
1461207247Sdelphij		remove_file = outfile;
1462207247Sdelphij#endif
1463166255Sdelphij	} else
1464166255Sdelphij		zfd = STDOUT_FILENO;
1465166255Sdelphij
1466226184Sdelphij	switch (method) {
1467166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1468226184Sdelphij	case FT_BZIP2:
1469166255Sdelphij		/* XXX */
1470166255Sdelphij		if (lflag) {
1471166255Sdelphij			maybe_warnx("no -l with bzip2 files");
1472166255Sdelphij			goto lose;
1473166255Sdelphij		}
1474166255Sdelphij
1475166255Sdelphij		size = unbzip2(fd, zfd, NULL, 0, NULL);
1476226184Sdelphij		break;
1477166255Sdelphij#endif
1478166255Sdelphij
1479166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1480226184Sdelphij	case FT_Z: {
1481166255Sdelphij		FILE *in, *out;
1482166255Sdelphij
1483166255Sdelphij		/* XXX */
1484166255Sdelphij		if (lflag) {
1485166255Sdelphij			maybe_warnx("no -l with Lempel-Ziv files");
1486166255Sdelphij			goto lose;
1487166255Sdelphij		}
1488166255Sdelphij
1489166255Sdelphij		if ((in = zdopen(fd)) == NULL) {
1490166255Sdelphij			maybe_warn("zdopen for read: %s", file);
1491166255Sdelphij			goto lose;
1492166255Sdelphij		}
1493166255Sdelphij
1494166255Sdelphij		out = fdopen(dup(zfd), "w");
1495166255Sdelphij		if (out == NULL) {
1496166255Sdelphij			maybe_warn("fdopen for write: %s", outfile);
1497166255Sdelphij			fclose(in);
1498166255Sdelphij			goto lose;
1499166255Sdelphij		}
1500166255Sdelphij
1501166255Sdelphij		size = zuncompress(in, out, NULL, 0, NULL);
1502166255Sdelphij		/* need to fclose() if ferror() is true... */
1503166255Sdelphij		if (ferror(in) | fclose(in)) {
1504166255Sdelphij			maybe_warn("failed infile fclose");
1505166255Sdelphij			unlink(outfile);
1506166255Sdelphij			(void)fclose(out);
1507166255Sdelphij		}
1508166255Sdelphij		if (fclose(out) != 0) {
1509166255Sdelphij			maybe_warn("failed outfile fclose");
1510166255Sdelphij			unlink(outfile);
1511166255Sdelphij			goto lose;
1512166255Sdelphij		}
1513226184Sdelphij		break;
1514226184Sdelphij	}
1515166255Sdelphij#endif
1516166255Sdelphij
1517194579Sdelphij#ifndef NO_PACK_SUPPORT
1518226184Sdelphij	case FT_PACK:
1519194579Sdelphij		if (lflag) {
1520194579Sdelphij			maybe_warnx("no -l with packed files");
1521194579Sdelphij			goto lose;
1522194579Sdelphij		}
1523194579Sdelphij
1524194579Sdelphij		size = unpack(fd, zfd, NULL, 0, NULL);
1525226184Sdelphij		break;
1526194579Sdelphij#endif
1527194579Sdelphij
1528226184Sdelphij#ifndef NO_XZ_SUPPORT
1529226184Sdelphij	case FT_XZ:
1530226184Sdelphij		if (lflag) {
1531226184Sdelphij			maybe_warnx("no -l with xz files");
1532226184Sdelphij			goto lose;
1533226184Sdelphij		}
1534226184Sdelphij
1535226184Sdelphij		size = unxz(fd, zfd, NULL, 0, NULL);
1536226184Sdelphij		break;
1537226184Sdelphij#endif
1538226184Sdelphij
1539166255Sdelphij#ifndef SMALL
1540226184Sdelphij	case FT_UNKNOWN:
1541166255Sdelphij		if (lflag) {
1542166255Sdelphij			maybe_warnx("no -l for unknown filetypes");
1543166255Sdelphij			goto lose;
1544166255Sdelphij		}
1545166255Sdelphij		size = cat_fd(NULL, 0, NULL, fd);
1546226184Sdelphij		break;
1547166255Sdelphij#endif
1548226184Sdelphij	default:
1549166255Sdelphij		if (lflag) {
1550166255Sdelphij			print_list(fd, isb.st_size, outfile, isb.st_mtime);
1551166255Sdelphij			close(fd);
1552166255Sdelphij			return -1;	/* XXX */
1553166255Sdelphij		}
1554166255Sdelphij
1555166255Sdelphij		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1556226184Sdelphij		break;
1557166255Sdelphij	}
1558166255Sdelphij
1559166255Sdelphij	if (close(fd) != 0)
1560166255Sdelphij		maybe_warn("couldn't close input");
1561166255Sdelphij	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1562166255Sdelphij		maybe_warn("couldn't close output");
1563166255Sdelphij
1564166255Sdelphij	if (size == -1) {
1565166255Sdelphij		if (cflag == 0)
1566166255Sdelphij			unlink(outfile);
1567166255Sdelphij		maybe_warnx("%s: uncompress failed", file);
1568166255Sdelphij		return -1;
1569166255Sdelphij	}
1570166255Sdelphij
1571166255Sdelphij	/* if testing, or we uncompressed to stdout, this is all we need */
1572166255Sdelphij#ifndef SMALL
1573166255Sdelphij	if (tflag)
1574166255Sdelphij		return size;
1575166255Sdelphij#endif
1576166255Sdelphij	/* if we are uncompressing to stdin, don't remove the file. */
1577166255Sdelphij	if (cflag)
1578166255Sdelphij		return size;
1579166255Sdelphij
1580166255Sdelphij	/*
1581166255Sdelphij	 * if we create a file...
1582166255Sdelphij	 */
1583166255Sdelphij	/*
1584166255Sdelphij	 * if we can't stat the file don't remove the file.
1585166255Sdelphij	 */
1586166255Sdelphij
1587166255Sdelphij	ofd = open(outfile, O_RDWR, 0);
1588166255Sdelphij	if (ofd == -1) {
1589166255Sdelphij		maybe_warn("couldn't open (leaving original): %s",
1590166255Sdelphij			   outfile);
1591166255Sdelphij		return -1;
1592166255Sdelphij	}
1593166255Sdelphij	if (fstat(ofd, &osb) != 0) {
1594166255Sdelphij		maybe_warn("couldn't stat (leaving original): %s",
1595166255Sdelphij			   outfile);
1596166255Sdelphij		close(ofd);
1597166255Sdelphij		return -1;
1598166255Sdelphij	}
1599166255Sdelphij	if (osb.st_size != size) {
1600209017Sdelphij		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1601209017Sdelphij		    (uintmax_t)size, (uintmax_t)osb.st_size);
1602166255Sdelphij		close(ofd);
1603166255Sdelphij		unlink(outfile);
1604166255Sdelphij		return -1;
1605166255Sdelphij	}
1606166255Sdelphij#ifndef SMALL
1607166255Sdelphij	copymodes(ofd, &isb, outfile);
1608207247Sdelphij	remove_file = NULL;
1609166255Sdelphij#endif
1610166255Sdelphij	close(ofd);
1611207247Sdelphij	unlink_input(file, &isb);
1612166255Sdelphij	return size;
1613166255Sdelphij
1614166255Sdelphij    unexpected_EOF:
1615166255Sdelphij	maybe_warnx("%s: unexpected end of file", file);
1616166255Sdelphij    lose:
1617166255Sdelphij	if (fd != -1)
1618166255Sdelphij		close(fd);
1619166255Sdelphij	if (zfd != -1 && zfd != STDOUT_FILENO)
1620166255Sdelphij		close(fd);
1621166255Sdelphij	return -1;
1622166255Sdelphij}
1623166255Sdelphij
1624166255Sdelphij#ifndef SMALL
1625166255Sdelphijstatic off_t
1626166255Sdelphijcat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1627166255Sdelphij{
1628166255Sdelphij	char buf[BUFLEN];
1629166255Sdelphij	off_t in_tot;
1630166255Sdelphij	ssize_t w;
1631166255Sdelphij
1632166255Sdelphij	in_tot = count;
1633166255Sdelphij	w = write(STDOUT_FILENO, prepend, count);
1634166255Sdelphij	if (w == -1 || (size_t)w != count) {
1635166255Sdelphij		maybe_warn("write to stdout");
1636166255Sdelphij		return -1;
1637166255Sdelphij	}
1638166255Sdelphij	for (;;) {
1639166255Sdelphij		ssize_t rv;
1640166255Sdelphij
1641166255Sdelphij		rv = read(fd, buf, sizeof buf);
1642166255Sdelphij		if (rv == 0)
1643166255Sdelphij			break;
1644166255Sdelphij		if (rv < 0) {
1645166255Sdelphij			maybe_warn("read from fd %d", fd);
1646166255Sdelphij			break;
1647166255Sdelphij		}
1648166255Sdelphij
1649166255Sdelphij		if (write(STDOUT_FILENO, buf, rv) != rv) {
1650166255Sdelphij			maybe_warn("write to stdout");
1651166255Sdelphij			break;
1652166255Sdelphij		}
1653166255Sdelphij		in_tot += rv;
1654166255Sdelphij	}
1655166255Sdelphij
1656166255Sdelphij	if (gsizep)
1657166255Sdelphij		*gsizep = in_tot;
1658166255Sdelphij	return (in_tot);
1659166255Sdelphij}
1660166255Sdelphij#endif
1661166255Sdelphij
1662166255Sdelphijstatic void
1663166255Sdelphijhandle_stdin(void)
1664166255Sdelphij{
1665166255Sdelphij	unsigned char header1[4];
1666166255Sdelphij	off_t usize, gsize;
1667166255Sdelphij	enum filetype method;
1668166255Sdelphij	ssize_t bytes_read;
1669166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1670166255Sdelphij	FILE *in;
1671166255Sdelphij#endif
1672166255Sdelphij
1673166255Sdelphij#ifndef SMALL
1674166255Sdelphij	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1675166255Sdelphij		maybe_warnx("standard input is a terminal -- ignoring");
1676166255Sdelphij		return;
1677166255Sdelphij	}
1678166255Sdelphij#endif
1679166255Sdelphij
1680166255Sdelphij	if (lflag) {
1681166255Sdelphij		struct stat isb;
1682166255Sdelphij
1683166255Sdelphij		/* XXX could read the whole file, etc. */
1684166255Sdelphij		if (fstat(STDIN_FILENO, &isb) < 0) {
1685166255Sdelphij			maybe_warn("fstat");
1686166255Sdelphij			return;
1687166255Sdelphij		}
1688166255Sdelphij		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1689166255Sdelphij		return;
1690166255Sdelphij	}
1691166255Sdelphij
1692166255Sdelphij	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1693166255Sdelphij	if (bytes_read == -1) {
1694166255Sdelphij		maybe_warn("can't read stdin");
1695166255Sdelphij		return;
1696166255Sdelphij	} else if (bytes_read != sizeof(header1)) {
1697166255Sdelphij		maybe_warnx("(stdin): unexpected end of file");
1698166255Sdelphij		return;
1699166255Sdelphij	}
1700166255Sdelphij
1701166255Sdelphij	method = file_gettype(header1);
1702166255Sdelphij	switch (method) {
1703166255Sdelphij	default:
1704166255Sdelphij#ifndef SMALL
1705166255Sdelphij		if (fflag == 0) {
1706166255Sdelphij			maybe_warnx("unknown compression format");
1707166255Sdelphij			return;
1708166255Sdelphij		}
1709166255Sdelphij		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1710166255Sdelphij		break;
1711166255Sdelphij#endif
1712166255Sdelphij	case FT_GZIP:
1713166255Sdelphij		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1714166255Sdelphij			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1715166255Sdelphij		break;
1716166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1717166255Sdelphij	case FT_BZIP2:
1718166255Sdelphij		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1719166255Sdelphij				(char *)header1, sizeof header1, &gsize);
1720166255Sdelphij		break;
1721166255Sdelphij#endif
1722166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1723166255Sdelphij	case FT_Z:
1724166255Sdelphij		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1725166255Sdelphij			maybe_warnx("zopen of stdin");
1726166255Sdelphij			return;
1727166255Sdelphij		}
1728166255Sdelphij
1729226184Sdelphij		usize = zuncompress(in, stdout, (char *)header1,
1730226184Sdelphij		    sizeof header1, &gsize);
1731166255Sdelphij		fclose(in);
1732166255Sdelphij		break;
1733166255Sdelphij#endif
1734194579Sdelphij#ifndef NO_PACK_SUPPORT
1735194579Sdelphij	case FT_PACK:
1736194579Sdelphij		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1737194579Sdelphij			       (char *)header1, sizeof header1, &gsize);
1738194579Sdelphij		break;
1739194579Sdelphij#endif
1740226184Sdelphij#ifndef NO_XZ_SUPPORT
1741226184Sdelphij	case FT_XZ:
1742226184Sdelphij		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1743226184Sdelphij			     (char *)header1, sizeof header1, &gsize);
1744226184Sdelphij		break;
1745226184Sdelphij#endif
1746166255Sdelphij	}
1747166255Sdelphij
1748166255Sdelphij#ifndef SMALL
1749166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1750166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1751166255Sdelphij	if (vflag && tflag)
1752166255Sdelphij		print_test("(stdin)", usize != -1);
1753166255Sdelphij#endif
1754166255Sdelphij
1755166255Sdelphij}
1756166255Sdelphij
1757166255Sdelphijstatic void
1758166255Sdelphijhandle_stdout(void)
1759166255Sdelphij{
1760166255Sdelphij	off_t gsize, usize;
1761166255Sdelphij	struct stat sb;
1762166255Sdelphij	time_t systime;
1763166255Sdelphij	uint32_t mtime;
1764166255Sdelphij	int ret;
1765166255Sdelphij
1766166255Sdelphij#ifndef SMALL
1767166255Sdelphij	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1768166255Sdelphij		maybe_warnx("standard output is a terminal -- ignoring");
1769166255Sdelphij		return;
1770166255Sdelphij	}
1771166255Sdelphij#endif
1772166255Sdelphij	/* If stdin is a file use it's mtime, otherwise use current time */
1773166255Sdelphij	ret = fstat(STDIN_FILENO, &sb);
1774166255Sdelphij
1775166255Sdelphij#ifndef SMALL
1776166255Sdelphij	if (ret < 0) {
1777166255Sdelphij		maybe_warn("Can't stat stdin");
1778166255Sdelphij		return;
1779166255Sdelphij	}
1780166255Sdelphij#endif
1781166255Sdelphij
1782166255Sdelphij	if (S_ISREG(sb.st_mode))
1783166255Sdelphij		mtime = (uint32_t)sb.st_mtime;
1784166255Sdelphij	else {
1785166255Sdelphij		systime = time(NULL);
1786166255Sdelphij#ifndef SMALL
1787166255Sdelphij		if (systime == -1) {
1788166255Sdelphij			maybe_warn("time");
1789166255Sdelphij			return;
1790166255Sdelphij		}
1791166255Sdelphij#endif
1792166255Sdelphij		mtime = (uint32_t)systime;
1793166255Sdelphij	}
1794166255Sdelphij
1795166255Sdelphij	usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1796166255Sdelphij#ifndef SMALL
1797166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1798166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1799166255Sdelphij#endif
1800166255Sdelphij}
1801166255Sdelphij
1802166255Sdelphij/* do what is asked for, for the path name */
1803166255Sdelphijstatic void
1804166255Sdelphijhandle_pathname(char *path)
1805166255Sdelphij{
1806166255Sdelphij	char *opath = path, *s = NULL;
1807166255Sdelphij	ssize_t len;
1808166255Sdelphij	int slen;
1809166255Sdelphij	struct stat sb;
1810166255Sdelphij
1811166255Sdelphij	/* check for stdout/stdin */
1812166255Sdelphij	if (path[0] == '-' && path[1] == '\0') {
1813166255Sdelphij		if (dflag)
1814166255Sdelphij			handle_stdin();
1815166255Sdelphij		else
1816166255Sdelphij			handle_stdout();
1817166255Sdelphij		return;
1818166255Sdelphij	}
1819166255Sdelphij
1820166255Sdelphijretry:
1821222287Sdelphij	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1822222287Sdelphij	    lstat(path, &sb) != 0)) {
1823166255Sdelphij		/* lets try <path>.gz if we're decompressing */
1824166255Sdelphij		if (dflag && s == NULL && errno == ENOENT) {
1825166255Sdelphij			len = strlen(path);
1826166255Sdelphij			slen = suffixes[0].ziplen;
1827166255Sdelphij			s = malloc(len + slen + 1);
1828166255Sdelphij			if (s == NULL)
1829166255Sdelphij				maybe_err("malloc");
1830166255Sdelphij			memcpy(s, path, len);
1831166255Sdelphij			memcpy(s + len, suffixes[0].zipped, slen + 1);
1832166255Sdelphij			path = s;
1833166255Sdelphij			goto retry;
1834166255Sdelphij		}
1835166255Sdelphij		maybe_warn("can't stat: %s", opath);
1836166255Sdelphij		goto out;
1837166255Sdelphij	}
1838166255Sdelphij
1839166255Sdelphij	if (S_ISDIR(sb.st_mode)) {
1840166255Sdelphij#ifndef SMALL
1841166255Sdelphij		if (rflag)
1842166255Sdelphij			handle_dir(path);
1843166255Sdelphij		else
1844166255Sdelphij#endif
1845166255Sdelphij			maybe_warnx("%s is a directory", path);
1846166255Sdelphij		goto out;
1847166255Sdelphij	}
1848166255Sdelphij
1849166255Sdelphij	if (S_ISREG(sb.st_mode))
1850166255Sdelphij		handle_file(path, &sb);
1851166255Sdelphij	else
1852166255Sdelphij		maybe_warnx("%s is not a regular file", path);
1853166255Sdelphij
1854166255Sdelphijout:
1855166255Sdelphij	if (s)
1856166255Sdelphij		free(s);
1857166255Sdelphij}
1858166255Sdelphij
1859166255Sdelphij/* compress/decompress a file */
1860166255Sdelphijstatic void
1861166255Sdelphijhandle_file(char *file, struct stat *sbp)
1862166255Sdelphij{
1863166255Sdelphij	off_t usize, gsize;
1864166255Sdelphij	char	outfile[PATH_MAX];
1865166255Sdelphij
1866166255Sdelphij	infile = file;
1867166255Sdelphij	if (dflag) {
1868166255Sdelphij		usize = file_uncompress(file, outfile, sizeof(outfile));
1869166255Sdelphij#ifndef SMALL
1870166255Sdelphij		if (vflag && tflag)
1871166255Sdelphij			print_test(file, usize != -1);
1872166255Sdelphij#endif
1873166255Sdelphij		if (usize == -1)
1874166255Sdelphij			return;
1875166255Sdelphij		gsize = sbp->st_size;
1876166255Sdelphij	} else {
1877166255Sdelphij		gsize = file_compress(file, outfile, sizeof(outfile));
1878166255Sdelphij		if (gsize == -1)
1879166255Sdelphij			return;
1880166255Sdelphij		usize = sbp->st_size;
1881166255Sdelphij	}
1882166255Sdelphij
1883166255Sdelphij
1884166255Sdelphij#ifndef SMALL
1885166255Sdelphij	if (vflag && !tflag)
1886166255Sdelphij		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1887166255Sdelphij#endif
1888166255Sdelphij}
1889166255Sdelphij
1890166255Sdelphij#ifndef SMALL
1891166255Sdelphij/* this is used with -r to recursively descend directories */
1892166255Sdelphijstatic void
1893166255Sdelphijhandle_dir(char *dir)
1894166255Sdelphij{
1895166255Sdelphij	char *path_argv[2];
1896166255Sdelphij	FTS *fts;
1897166255Sdelphij	FTSENT *entry;
1898166255Sdelphij
1899166255Sdelphij	path_argv[0] = dir;
1900166255Sdelphij	path_argv[1] = 0;
1901171389Sdelphij	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1902166255Sdelphij	if (fts == NULL) {
1903166255Sdelphij		warn("couldn't fts_open %s", dir);
1904166255Sdelphij		return;
1905166255Sdelphij	}
1906166255Sdelphij
1907166255Sdelphij	while ((entry = fts_read(fts))) {
1908166255Sdelphij		switch(entry->fts_info) {
1909166255Sdelphij		case FTS_D:
1910166255Sdelphij		case FTS_DP:
1911166255Sdelphij			continue;
1912166255Sdelphij
1913166255Sdelphij		case FTS_DNR:
1914166255Sdelphij		case FTS_ERR:
1915166255Sdelphij		case FTS_NS:
1916166255Sdelphij			maybe_warn("%s", entry->fts_path);
1917166255Sdelphij			continue;
1918166255Sdelphij		case FTS_F:
1919171389Sdelphij			handle_file(entry->fts_path, entry->fts_statp);
1920166255Sdelphij		}
1921166255Sdelphij	}
1922166255Sdelphij	(void)fts_close(fts);
1923166255Sdelphij}
1924166255Sdelphij#endif
1925166255Sdelphij
1926166255Sdelphij/* print a ratio - size reduction as a fraction of uncompressed size */
1927166255Sdelphijstatic void
1928166255Sdelphijprint_ratio(off_t in, off_t out, FILE *where)
1929166255Sdelphij{
1930166255Sdelphij	int percent10;	/* 10 * percent */
1931166255Sdelphij	off_t diff;
1932166255Sdelphij	char buff[8];
1933166255Sdelphij	int len;
1934166255Sdelphij
1935166255Sdelphij	diff = in - out/2;
1936166255Sdelphij	if (diff <= 0)
1937166255Sdelphij		/*
1938166255Sdelphij		 * Output is more than double size of input! print -99.9%
1939166255Sdelphij		 * Quite possibly we've failed to get the original size.
1940166255Sdelphij		 */
1941166255Sdelphij		percent10 = -999;
1942166255Sdelphij	else {
1943166255Sdelphij		/*
1944166255Sdelphij		 * We only need 12 bits of result from the final division,
1945166255Sdelphij		 * so reduce the values until a 32bit division will suffice.
1946166255Sdelphij		 */
1947166255Sdelphij		while (in > 0x100000) {
1948166255Sdelphij			diff >>= 1;
1949166255Sdelphij			in >>= 1;
1950166255Sdelphij		}
1951166255Sdelphij		if (in != 0)
1952166255Sdelphij			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1953166255Sdelphij		else
1954166255Sdelphij			percent10 = 0;
1955166255Sdelphij	}
1956166255Sdelphij
1957166255Sdelphij	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1958166255Sdelphij	/* Move the '.' to before the last digit */
1959166255Sdelphij	buff[len - 1] = buff[len - 2];
1960166255Sdelphij	buff[len - 2] = '.';
1961166255Sdelphij	fprintf(where, "%5s%%", buff);
1962166255Sdelphij}
1963166255Sdelphij
1964166255Sdelphij#ifndef SMALL
1965166255Sdelphij/* print compression statistics, and the new name (if there is one!) */
1966166255Sdelphijstatic void
1967166255Sdelphijprint_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1968166255Sdelphij{
1969166255Sdelphij	if (file)
1970166255Sdelphij		fprintf(stderr, "%s:%s  ", file,
1971166255Sdelphij		    strlen(file) < 7 ? "\t\t" : "\t");
1972166255Sdelphij	print_ratio(usize, gsize, stderr);
1973166255Sdelphij	if (nfile)
1974166255Sdelphij		fprintf(stderr, " -- replaced with %s", nfile);
1975166255Sdelphij	fprintf(stderr, "\n");
1976166255Sdelphij	fflush(stderr);
1977166255Sdelphij}
1978166255Sdelphij
1979166255Sdelphij/* print test results */
1980166255Sdelphijstatic void
1981166255Sdelphijprint_test(const char *file, int ok)
1982166255Sdelphij{
1983166255Sdelphij
1984166255Sdelphij	if (exit_value == 0 && ok == 0)
1985166255Sdelphij		exit_value = 1;
1986166255Sdelphij	fprintf(stderr, "%s:%s  %s\n", file,
1987166255Sdelphij	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1988166255Sdelphij	fflush(stderr);
1989166255Sdelphij}
1990166255Sdelphij#endif
1991166255Sdelphij
1992166255Sdelphij/* print a file's info ala --list */
1993166255Sdelphij/* eg:
1994166255Sdelphij  compressed uncompressed  ratio uncompressed_name
1995166255Sdelphij      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1996166255Sdelphij*/
1997166255Sdelphijstatic void
1998166255Sdelphijprint_list(int fd, off_t out, const char *outfile, time_t ts)
1999166255Sdelphij{
2000166255Sdelphij	static int first = 1;
2001166255Sdelphij#ifndef SMALL
2002166255Sdelphij	static off_t in_tot, out_tot;
2003166255Sdelphij	uint32_t crc = 0;
2004166255Sdelphij#endif
2005166255Sdelphij	off_t in = 0, rv;
2006166255Sdelphij
2007166255Sdelphij	if (first) {
2008166255Sdelphij#ifndef SMALL
2009166255Sdelphij		if (vflag)
2010166255Sdelphij			printf("method  crc     date  time  ");
2011166255Sdelphij#endif
2012166255Sdelphij		if (qflag == 0)
2013166255Sdelphij			printf("  compressed uncompressed  "
2014166255Sdelphij			       "ratio uncompressed_name\n");
2015166255Sdelphij	}
2016166255Sdelphij	first = 0;
2017166255Sdelphij
2018166255Sdelphij	/* print totals? */
2019166255Sdelphij#ifndef SMALL
2020166255Sdelphij	if (fd == -1) {
2021166255Sdelphij		in = in_tot;
2022166255Sdelphij		out = out_tot;
2023166255Sdelphij	} else
2024166255Sdelphij#endif
2025166255Sdelphij	{
2026166255Sdelphij		/* read the last 4 bytes - this is the uncompressed size */
2027166255Sdelphij		rv = lseek(fd, (off_t)(-8), SEEK_END);
2028166255Sdelphij		if (rv != -1) {
2029166255Sdelphij			unsigned char buf[8];
2030166255Sdelphij			uint32_t usize;
2031166255Sdelphij
2032166255Sdelphij			rv = read(fd, (char *)buf, sizeof(buf));
2033166255Sdelphij			if (rv == -1)
2034166255Sdelphij				maybe_warn("read of uncompressed size");
2035166255Sdelphij			else if (rv != sizeof(buf))
2036166255Sdelphij				maybe_warnx("read of uncompressed size");
2037166255Sdelphij
2038166255Sdelphij			else {
2039166255Sdelphij				usize = buf[4] | buf[5] << 8 |
2040166255Sdelphij					buf[6] << 16 | buf[7] << 24;
2041166255Sdelphij				in = (off_t)usize;
2042166255Sdelphij#ifndef SMALL
2043166255Sdelphij				crc = buf[0] | buf[1] << 8 |
2044166255Sdelphij				      buf[2] << 16 | buf[3] << 24;
2045166255Sdelphij#endif
2046166255Sdelphij			}
2047166255Sdelphij		}
2048166255Sdelphij	}
2049166255Sdelphij
2050166255Sdelphij#ifndef SMALL
2051166255Sdelphij	if (vflag && fd == -1)
2052166255Sdelphij		printf("                            ");
2053166255Sdelphij	else if (vflag) {
2054166255Sdelphij		char *date = ctime(&ts);
2055166255Sdelphij
2056166255Sdelphij		/* skip the day, 1/100th second, and year */
2057166255Sdelphij		date += 4;
2058166255Sdelphij		date[12] = 0;
2059166255Sdelphij		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2060166255Sdelphij	}
2061166255Sdelphij	in_tot += in;
2062166255Sdelphij	out_tot += out;
2063194508Sdelphij#else
2064194508Sdelphij	(void)&ts;	/* XXX */
2065166255Sdelphij#endif
2066166255Sdelphij	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2067166255Sdelphij	print_ratio(in, out, stdout);
2068166255Sdelphij	printf(" %s\n", outfile);
2069166255Sdelphij}
2070166255Sdelphij
2071166255Sdelphij/* display the usage of NetBSD gzip */
2072166255Sdelphijstatic void
2073166255Sdelphijusage(void)
2074166255Sdelphij{
2075166255Sdelphij
2076166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2077166255Sdelphij	fprintf(stderr,
2078171389Sdelphij#ifdef SMALL
2079171389Sdelphij    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2080171389Sdelphij#else
2081171389Sdelphij    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2082166255Sdelphij    " -1 --fast            fastest (worst) compression\n"
2083166255Sdelphij    " -2 .. -8             set compression level\n"
2084166255Sdelphij    " -9 --best            best (slowest) compression\n"
2085166255Sdelphij    " -c --stdout          write to stdout, keep original files\n"
2086166255Sdelphij    "    --to-stdout\n"
2087166255Sdelphij    " -d --decompress      uncompress files\n"
2088166255Sdelphij    "    --uncompress\n"
2089166255Sdelphij    " -f --force           force overwriting & compress links\n"
2090166255Sdelphij    " -h --help            display this help\n"
2091170053Sdelphij    " -k --keep            don't delete input files during operation\n"
2092166255Sdelphij    " -l --list            list compressed file contents\n"
2093166255Sdelphij    " -N --name            save or restore original file name and time stamp\n"
2094166255Sdelphij    " -n --no-name         don't save original file name or time stamp\n"
2095166255Sdelphij    " -q --quiet           output no warnings\n"
2096166255Sdelphij    " -r --recursive       recursively compress files in directories\n"
2097166255Sdelphij    " -S .suf              use suffix .suf instead of .gz\n"
2098166255Sdelphij    "    --suffix .suf\n"
2099166255Sdelphij    " -t --test            test compressed file\n"
2100166255Sdelphij    " -V --version         display program version\n"
2101166255Sdelphij    " -v --verbose         print extra statistics\n",
2102166255Sdelphij#endif
2103166255Sdelphij	    getprogname());
2104166255Sdelphij	exit(0);
2105166255Sdelphij}
2106166255Sdelphij
2107166255Sdelphij#ifndef SMALL
2108166255Sdelphij/* display the license information of FreeBSD gzip */
2109166255Sdelphijstatic void
2110166255Sdelphijdisplay_license(void)
2111166255Sdelphij{
2112166255Sdelphij
2113226184Sdelphij	fprintf(stderr, "%s (based on NetBSD gzip 20111009)\n", gzip_version);
2114166255Sdelphij	fprintf(stderr, "%s\n", gzip_copyright);
2115166255Sdelphij	exit(0);
2116166255Sdelphij}
2117166255Sdelphij#endif
2118166255Sdelphij
2119166255Sdelphij/* display the version of NetBSD gzip */
2120166255Sdelphijstatic void
2121166255Sdelphijdisplay_version(void)
2122166255Sdelphij{
2123166255Sdelphij
2124166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2125166255Sdelphij	exit(0);
2126166255Sdelphij}
2127166255Sdelphij
2128166255Sdelphij#ifndef NO_BZIP2_SUPPORT
2129166255Sdelphij#include "unbzip2.c"
2130166255Sdelphij#endif
2131166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
2132166255Sdelphij#include "zuncompress.c"
2133166255Sdelphij#endif
2134194579Sdelphij#ifndef NO_PACK_SUPPORT
2135194579Sdelphij#include "unpack.c"
2136194579Sdelphij#endif
2137226184Sdelphij#ifndef NO_XZ_SUPPORT
2138226184Sdelphij#include "unxz.c"
2139226184Sdelphij#endif
2140166255Sdelphij
2141166255Sdelphijstatic ssize_t
2142166255Sdelphijread_retry(int fd, void *buf, size_t sz)
2143166255Sdelphij{
2144166255Sdelphij	char *cp = buf;
2145166255Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2146166255Sdelphij
2147166255Sdelphij	while (left > 0) {
2148166255Sdelphij		ssize_t ret;
2149166255Sdelphij
2150166255Sdelphij		ret = read(fd, cp, left);
2151166255Sdelphij		if (ret == -1) {
2152166255Sdelphij			return ret;
2153166255Sdelphij		} else if (ret == 0) {
2154166255Sdelphij			break; /* EOF */
2155166255Sdelphij		}
2156166255Sdelphij		cp += ret;
2157166255Sdelphij		left -= ret;
2158166255Sdelphij	}
2159166255Sdelphij
2160166255Sdelphij	return sz - left;
2161166255Sdelphij}
2162