gzip.c revision 290024
1281626Sdelphij/*	$NetBSD: gzip.c,v 1.108 2015/04/15 02:29:12 christos 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: head/usr.bin/gzip/gzip.c 290024 2015-10-26 22:29:58Z delphij $");
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
161281500Sdelphijstatic	const char	gzip_version[] = "FreeBSD gzip 20150413";
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);
813290024Sdelphij					exit_value = 2;
814166255Sdelphij					goto stop;
815166255Sdelphij				}
816166255Sdelphij				maybe_warnx("input not gziped (MAGIC0)");
817166255Sdelphij				goto stop_and_fail;
818166255Sdelphij			}
819166255Sdelphij			ADVANCE();
820166255Sdelphij			state++;
821166255Sdelphij			out_sub_tot = 0;
822166255Sdelphij			crc = crc32(0L, Z_NULL, 0);
823166255Sdelphij			break;
824166255Sdelphij
825166255Sdelphij		case GZSTATE_MAGIC1:
826166255Sdelphij			if (*z.next_in != GZIP_MAGIC1 &&
827166255Sdelphij			    *z.next_in != GZIP_OMAGIC1) {
828166255Sdelphij				maybe_warnx("input not gziped (MAGIC1)");
829166255Sdelphij				goto stop_and_fail;
830166255Sdelphij			}
831166255Sdelphij			ADVANCE();
832166255Sdelphij			state++;
833166255Sdelphij			break;
834166255Sdelphij
835166255Sdelphij		case GZSTATE_METHOD:
836166255Sdelphij			if (*z.next_in != Z_DEFLATED) {
837166255Sdelphij				maybe_warnx("unknown compression method");
838166255Sdelphij				goto stop_and_fail;
839166255Sdelphij			}
840166255Sdelphij			ADVANCE();
841166255Sdelphij			state++;
842166255Sdelphij			break;
843166255Sdelphij
844166255Sdelphij		case GZSTATE_FLAGS:
845166255Sdelphij			flags = *z.next_in;
846166255Sdelphij			ADVANCE();
847166255Sdelphij			skip_count = 6;
848166255Sdelphij			state++;
849166255Sdelphij			break;
850166255Sdelphij
851166255Sdelphij		case GZSTATE_SKIPPING:
852166255Sdelphij			if (skip_count > 0) {
853166255Sdelphij				skip_count--;
854166255Sdelphij				ADVANCE();
855166255Sdelphij			} else
856166255Sdelphij				state++;
857166255Sdelphij			break;
858166255Sdelphij
859166255Sdelphij		case GZSTATE_EXTRA:
860166255Sdelphij			if ((flags & EXTRA_FIELD) == 0) {
861166255Sdelphij				state = GZSTATE_ORIGNAME;
862166255Sdelphij				break;
863166255Sdelphij			}
864166255Sdelphij			skip_count = *z.next_in;
865166255Sdelphij			ADVANCE();
866166255Sdelphij			state++;
867166255Sdelphij			break;
868166255Sdelphij
869166255Sdelphij		case GZSTATE_EXTRA2:
870166255Sdelphij			skip_count |= ((*z.next_in) << 8);
871166255Sdelphij			ADVANCE();
872166255Sdelphij			state++;
873166255Sdelphij			break;
874166255Sdelphij
875166255Sdelphij		case GZSTATE_EXTRA3:
876166255Sdelphij			if (skip_count > 0) {
877166255Sdelphij				skip_count--;
878166255Sdelphij				ADVANCE();
879166255Sdelphij			} else
880166255Sdelphij				state++;
881166255Sdelphij			break;
882166255Sdelphij
883166255Sdelphij		case GZSTATE_ORIGNAME:
884166255Sdelphij			if ((flags & ORIG_NAME) == 0) {
885166255Sdelphij				state++;
886166255Sdelphij				break;
887166255Sdelphij			}
888166255Sdelphij			if (*z.next_in == 0)
889166255Sdelphij				state++;
890166255Sdelphij			ADVANCE();
891166255Sdelphij			break;
892166255Sdelphij
893166255Sdelphij		case GZSTATE_COMMENT:
894166255Sdelphij			if ((flags & COMMENT) == 0) {
895166255Sdelphij				state++;
896166255Sdelphij				break;
897166255Sdelphij			}
898166255Sdelphij			if (*z.next_in == 0)
899166255Sdelphij				state++;
900166255Sdelphij			ADVANCE();
901166255Sdelphij			break;
902166255Sdelphij
903166255Sdelphij		case GZSTATE_HEAD_CRC1:
904166255Sdelphij			if (flags & HEAD_CRC)
905166255Sdelphij				skip_count = 2;
906166255Sdelphij			else
907166255Sdelphij				skip_count = 0;
908166255Sdelphij			state++;
909166255Sdelphij			break;
910166255Sdelphij
911166255Sdelphij		case GZSTATE_HEAD_CRC2:
912166255Sdelphij			if (skip_count > 0) {
913166255Sdelphij				skip_count--;
914166255Sdelphij				ADVANCE();
915166255Sdelphij			} else
916166255Sdelphij				state++;
917166255Sdelphij			break;
918166255Sdelphij
919166255Sdelphij		case GZSTATE_INIT:
920166255Sdelphij			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
921166255Sdelphij				maybe_warnx("failed to inflateInit");
922166255Sdelphij				goto stop_and_fail;
923166255Sdelphij			}
924166255Sdelphij			state++;
925166255Sdelphij			break;
926166255Sdelphij
927166255Sdelphij		case GZSTATE_READ:
928166255Sdelphij			error = inflate(&z, Z_FINISH);
929166255Sdelphij			switch (error) {
930166255Sdelphij			/* Z_BUF_ERROR goes with Z_FINISH... */
931166255Sdelphij			case Z_BUF_ERROR:
932213044Sdelphij				if (z.avail_out > 0 && !done_reading)
933213044Sdelphij					continue;
934222210Sdelphij
935166255Sdelphij			case Z_STREAM_END:
936166255Sdelphij			case Z_OK:
937166255Sdelphij				break;
938166255Sdelphij
939166255Sdelphij			case Z_NEED_DICT:
940166255Sdelphij				maybe_warnx("Z_NEED_DICT error");
941166255Sdelphij				goto stop_and_fail;
942166255Sdelphij			case Z_DATA_ERROR:
943166255Sdelphij				maybe_warnx("data stream error");
944166255Sdelphij				goto stop_and_fail;
945166255Sdelphij			case Z_STREAM_ERROR:
946166255Sdelphij				maybe_warnx("internal stream error");
947166255Sdelphij				goto stop_and_fail;
948166255Sdelphij			case Z_MEM_ERROR:
949166255Sdelphij				maybe_warnx("memory allocation error");
950166255Sdelphij				goto stop_and_fail;
951166255Sdelphij
952166255Sdelphij			default:
953166255Sdelphij				maybe_warn("unknown error from inflate(): %d",
954166255Sdelphij				    error);
955166255Sdelphij			}
956166255Sdelphij			wr = BUFLEN - z.avail_out;
957166255Sdelphij
958166255Sdelphij			if (wr != 0) {
959166255Sdelphij				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
960166255Sdelphij				if (
961166255Sdelphij#ifndef SMALL
962166255Sdelphij				    /* don't write anything with -t */
963166255Sdelphij				    tflag == 0 &&
964166255Sdelphij#endif
965166255Sdelphij				    write(out, outbufp, wr) != wr) {
966166255Sdelphij					maybe_warn("error writing to output");
967166255Sdelphij					goto stop_and_fail;
968166255Sdelphij				}
969166255Sdelphij
970166255Sdelphij				out_tot += wr;
971166255Sdelphij				out_sub_tot += wr;
972166255Sdelphij			}
973166255Sdelphij
974166255Sdelphij			if (error == Z_STREAM_END) {
975166255Sdelphij				inflateEnd(&z);
976166255Sdelphij				state++;
977166255Sdelphij			}
978166255Sdelphij
979166255Sdelphij			z.next_out = (unsigned char *)outbufp;
980166255Sdelphij			z.avail_out = BUFLEN;
981166255Sdelphij
982166255Sdelphij			break;
983166255Sdelphij		case GZSTATE_CRC:
984166255Sdelphij			{
985166255Sdelphij				uLong origcrc;
986166255Sdelphij
987166255Sdelphij				if (z.avail_in < 4) {
988166255Sdelphij					if (!done_reading) {
989166255Sdelphij						needmore = 1;
990166255Sdelphij						continue;
991166255Sdelphij					}
992166255Sdelphij					maybe_warnx("truncated input");
993166255Sdelphij					goto stop_and_fail;
994166255Sdelphij				}
995166255Sdelphij				origcrc = ((unsigned)z.next_in[0] & 0xff) |
996166255Sdelphij					((unsigned)z.next_in[1] & 0xff) << 8 |
997166255Sdelphij					((unsigned)z.next_in[2] & 0xff) << 16 |
998166255Sdelphij					((unsigned)z.next_in[3] & 0xff) << 24;
999166255Sdelphij				if (origcrc != crc) {
1000166255Sdelphij					maybe_warnx("invalid compressed"
1001166255Sdelphij					     " data--crc error");
1002166255Sdelphij					goto stop_and_fail;
1003166255Sdelphij				}
1004166255Sdelphij			}
1005166255Sdelphij
1006166255Sdelphij			z.avail_in -= 4;
1007166255Sdelphij			z.next_in += 4;
1008166255Sdelphij
1009166255Sdelphij			if (!z.avail_in && done_reading) {
1010166255Sdelphij				goto stop;
1011166255Sdelphij			}
1012166255Sdelphij			state++;
1013166255Sdelphij			break;
1014166255Sdelphij		case GZSTATE_LEN:
1015166255Sdelphij			{
1016166255Sdelphij				uLong origlen;
1017166255Sdelphij
1018166255Sdelphij				if (z.avail_in < 4) {
1019166255Sdelphij					if (!done_reading) {
1020166255Sdelphij						needmore = 1;
1021166255Sdelphij						continue;
1022166255Sdelphij					}
1023166255Sdelphij					maybe_warnx("truncated input");
1024166255Sdelphij					goto stop_and_fail;
1025166255Sdelphij				}
1026166255Sdelphij				origlen = ((unsigned)z.next_in[0] & 0xff) |
1027166255Sdelphij					((unsigned)z.next_in[1] & 0xff) << 8 |
1028166255Sdelphij					((unsigned)z.next_in[2] & 0xff) << 16 |
1029166255Sdelphij					((unsigned)z.next_in[3] & 0xff) << 24;
1030166255Sdelphij
1031166255Sdelphij				if (origlen != out_sub_tot) {
1032166255Sdelphij					maybe_warnx("invalid compressed"
1033166255Sdelphij					     " data--length error");
1034166255Sdelphij					goto stop_and_fail;
1035166255Sdelphij				}
1036166255Sdelphij			}
1037166255Sdelphij
1038166255Sdelphij			z.avail_in -= 4;
1039166255Sdelphij			z.next_in += 4;
1040166255Sdelphij
1041166255Sdelphij			if (error < 0) {
1042166255Sdelphij				maybe_warnx("decompression error");
1043166255Sdelphij				goto stop_and_fail;
1044166255Sdelphij			}
1045166255Sdelphij			state = GZSTATE_MAGIC0;
1046166255Sdelphij			break;
1047166255Sdelphij		}
1048166255Sdelphij		continue;
1049166255Sdelphijstop_and_fail:
1050166255Sdelphij		out_tot = -1;
1051166255Sdelphijstop:
1052166255Sdelphij		break;
1053166255Sdelphij	}
1054166255Sdelphij	if (state > GZSTATE_INIT)
1055166255Sdelphij		inflateEnd(&z);
1056166255Sdelphij
1057166255Sdelphij	free(inbufp);
1058166255Sdelphijout1:
1059166255Sdelphij	free(outbufp);
1060166255Sdelphijout2:
1061166255Sdelphij	if (gsizep)
1062166255Sdelphij		*gsizep = in_tot;
1063166255Sdelphij	return (out_tot);
1064166255Sdelphij}
1065166255Sdelphij
1066166255Sdelphij#ifndef SMALL
1067166255Sdelphij/*
1068166255Sdelphij * set the owner, mode, flags & utimes using the given file descriptor.
1069166255Sdelphij * file is only used in possible warning messages.
1070166255Sdelphij */
1071166255Sdelphijstatic void
1072166255Sdelphijcopymodes(int fd, const struct stat *sbp, const char *file)
1073166255Sdelphij{
1074278896Sjilles	struct timespec times[2];
1075166255Sdelphij	struct stat sb;
1076166255Sdelphij
1077166255Sdelphij	/*
1078166255Sdelphij	 * If we have no info on the input, give this file some
1079166255Sdelphij	 * default values and return..
1080166255Sdelphij	 */
1081166255Sdelphij	if (sbp == NULL) {
1082166255Sdelphij		mode_t mask = umask(022);
1083166255Sdelphij
1084166255Sdelphij		(void)fchmod(fd, DEFFILEMODE & ~mask);
1085166255Sdelphij		(void)umask(mask);
1086166255Sdelphij		return;
1087166255Sdelphij	}
1088166255Sdelphij	sb = *sbp;
1089166255Sdelphij
1090166255Sdelphij	/* if the chown fails, remove set-id bits as-per compress(1) */
1091166255Sdelphij	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1092166255Sdelphij		if (errno != EPERM)
1093166255Sdelphij			maybe_warn("couldn't fchown: %s", file);
1094166255Sdelphij		sb.st_mode &= ~(S_ISUID|S_ISGID);
1095166255Sdelphij	}
1096166255Sdelphij
1097166255Sdelphij	/* we only allow set-id and the 9 normal permission bits */
1098166255Sdelphij	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1099166255Sdelphij	if (fchmod(fd, sb.st_mode) < 0)
1100166255Sdelphij		maybe_warn("couldn't fchmod: %s", file);
1101166255Sdelphij
1102278896Sjilles	times[0] = sb.st_atim;
1103278896Sjilles	times[1] = sb.st_mtim;
1104278896Sjilles	if (futimens(fd, times) < 0)
1105278896Sjilles		maybe_warn("couldn't futimens: %s", file);
1106176970Srwatson
1107176970Srwatson	/* only try flags if they exist already */
1108176970Srwatson        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1109176970Srwatson		maybe_warn("couldn't fchflags: %s", file);
1110166255Sdelphij}
1111166255Sdelphij#endif
1112166255Sdelphij
1113166255Sdelphij/* what sort of file is this? */
1114166255Sdelphijstatic enum filetype
1115166255Sdelphijfile_gettype(u_char *buf)
1116166255Sdelphij{
1117166255Sdelphij
1118166255Sdelphij	if (buf[0] == GZIP_MAGIC0 &&
1119166255Sdelphij	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1120166255Sdelphij		return FT_GZIP;
1121166255Sdelphij	else
1122166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1123166255Sdelphij	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1124166255Sdelphij	    buf[3] >= '0' && buf[3] <= '9')
1125166255Sdelphij		return FT_BZIP2;
1126166255Sdelphij	else
1127166255Sdelphij#endif
1128166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1129166255Sdelphij	if (memcmp(buf, Z_MAGIC, 2) == 0)
1130166255Sdelphij		return FT_Z;
1131166255Sdelphij	else
1132166255Sdelphij#endif
1133194579Sdelphij#ifndef NO_PACK_SUPPORT
1134194579Sdelphij	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1135194579Sdelphij		return FT_PACK;
1136194579Sdelphij	else
1137194579Sdelphij#endif
1138226184Sdelphij#ifndef NO_XZ_SUPPORT
1139226184Sdelphij	if (memcmp(buf, XZ_MAGIC, 4) == 0)	/* XXX: We only have 4 bytes */
1140226184Sdelphij		return FT_XZ;
1141226184Sdelphij	else
1142226184Sdelphij#endif
1143166255Sdelphij		return FT_UNKNOWN;
1144166255Sdelphij}
1145166255Sdelphij
1146166255Sdelphij#ifndef SMALL
1147166255Sdelphij/* check the outfile is OK. */
1148166255Sdelphijstatic int
1149166255Sdelphijcheck_outfile(const char *outfile)
1150166255Sdelphij{
1151166255Sdelphij	struct stat sb;
1152166255Sdelphij	int ok = 1;
1153166255Sdelphij
1154166255Sdelphij	if (lflag == 0 && stat(outfile, &sb) == 0) {
1155166255Sdelphij		if (fflag)
1156166255Sdelphij			unlink(outfile);
1157166255Sdelphij		else if (isatty(STDIN_FILENO)) {
1158166255Sdelphij			char ans[10] = { 'n', '\0' };	/* default */
1159166255Sdelphij
1160166255Sdelphij			fprintf(stderr, "%s already exists -- do you wish to "
1161166255Sdelphij					"overwrite (y or n)? " , outfile);
1162166255Sdelphij			(void)fgets(ans, sizeof(ans) - 1, stdin);
1163166255Sdelphij			if (ans[0] != 'y' && ans[0] != 'Y') {
1164166363Sdelphij				fprintf(stderr, "\tnot overwriting\n");
1165166255Sdelphij				ok = 0;
1166166255Sdelphij			} else
1167166255Sdelphij				unlink(outfile);
1168166255Sdelphij		} else {
1169166255Sdelphij			maybe_warnx("%s already exists -- skipping", outfile);
1170166255Sdelphij			ok = 0;
1171166255Sdelphij		}
1172166255Sdelphij	}
1173166255Sdelphij	return ok;
1174166255Sdelphij}
1175166255Sdelphij
1176166255Sdelphijstatic void
1177166255Sdelphijunlink_input(const char *file, const struct stat *sb)
1178166255Sdelphij{
1179166255Sdelphij	struct stat nsb;
1180166255Sdelphij
1181170053Sdelphij	if (kflag)
1182170053Sdelphij		return;
1183166255Sdelphij	if (stat(file, &nsb) != 0)
1184213927Sbcr		/* Must be gone already */
1185166255Sdelphij		return;
1186166255Sdelphij	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1187166255Sdelphij		/* Definitely a different file */
1188166255Sdelphij		return;
1189166255Sdelphij	unlink(file);
1190166255Sdelphij}
1191207247Sdelphij
1192207247Sdelphijstatic void
1193207247Sdelphijsigint_handler(int signo __unused)
1194207247Sdelphij{
1195207247Sdelphij
1196207247Sdelphij	if (remove_file != NULL)
1197207247Sdelphij		unlink(remove_file);
1198207284Sdelphij	_exit(2);
1199207247Sdelphij}
1200166255Sdelphij#endif
1201166255Sdelphij
1202166255Sdelphijstatic const suffixes_t *
1203166255Sdelphijcheck_suffix(char *file, int xlate)
1204166255Sdelphij{
1205166255Sdelphij	const suffixes_t *s;
1206166255Sdelphij	int len = strlen(file);
1207166255Sdelphij	char *sp;
1208166255Sdelphij
1209166255Sdelphij	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1210166255Sdelphij		/* if it doesn't fit in "a.suf", don't bother */
1211166255Sdelphij		if (s->ziplen >= len)
1212166255Sdelphij			continue;
1213166255Sdelphij		sp = file + len - s->ziplen;
1214166255Sdelphij		if (strcmp(s->zipped, sp) != 0)
1215166255Sdelphij			continue;
1216166255Sdelphij		if (xlate)
1217166255Sdelphij			strcpy(sp, s->normal);
1218166255Sdelphij		return s;
1219166255Sdelphij	}
1220166255Sdelphij	return NULL;
1221166255Sdelphij}
1222166255Sdelphij
1223166255Sdelphij/*
1224166255Sdelphij * compress the given file: create a corresponding .gz file and remove the
1225166255Sdelphij * original.
1226166255Sdelphij */
1227166255Sdelphijstatic off_t
1228166255Sdelphijfile_compress(char *file, char *outfile, size_t outsize)
1229166255Sdelphij{
1230166255Sdelphij	int in;
1231166255Sdelphij	int out;
1232166255Sdelphij	off_t size, insize;
1233166255Sdelphij#ifndef SMALL
1234166255Sdelphij	struct stat isb, osb;
1235166255Sdelphij	const suffixes_t *suff;
1236166255Sdelphij#endif
1237166255Sdelphij
1238166255Sdelphij	in = open(file, O_RDONLY);
1239166255Sdelphij	if (in == -1) {
1240166255Sdelphij		maybe_warn("can't open %s", file);
1241209017Sdelphij		return (-1);
1242166255Sdelphij	}
1243166255Sdelphij
1244208888Sdelphij#ifndef SMALL
1245208888Sdelphij	if (fstat(in, &isb) != 0) {
1246208888Sdelphij		maybe_warn("couldn't stat: %s", file);
1247208888Sdelphij		close(in);
1248209017Sdelphij		return (-1);
1249208888Sdelphij	}
1250208888Sdelphij#endif
1251208888Sdelphij
1252166255Sdelphij	if (cflag == 0) {
1253166255Sdelphij#ifndef SMALL
1254209017Sdelphij		if (isb.st_nlink > 1 && fflag == 0) {
1255209017Sdelphij			maybe_warnx("%s has %d other link%s -- skipping",
1256209017Sdelphij			    file, isb.st_nlink - 1,
1257209017Sdelphij			    (isb.st_nlink - 1) == 1 ? "" : "s");
1258208889Sdelphij			close(in);
1259209017Sdelphij			return (-1);
1260208889Sdelphij		}
1261166255Sdelphij
1262209017Sdelphij		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1263209017Sdelphij		    suff->zipped[0] != 0) {
1264166255Sdelphij			maybe_warnx("%s already has %s suffix -- unchanged",
1265209017Sdelphij			    file, suff->zipped);
1266166255Sdelphij			close(in);
1267209017Sdelphij			return (-1);
1268166255Sdelphij		}
1269166255Sdelphij#endif
1270166255Sdelphij
1271166255Sdelphij		/* Add (usually) .gz to filename */
1272166255Sdelphij		if ((size_t)snprintf(outfile, outsize, "%s%s",
1273209017Sdelphij		    file, suffixes[0].zipped) >= outsize)
1274195988Sdelphij			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1275209017Sdelphij			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1276166255Sdelphij
1277166255Sdelphij#ifndef SMALL
1278166255Sdelphij		if (check_outfile(outfile) == 0) {
1279166255Sdelphij			close(in);
1280209017Sdelphij			return (-1);
1281166255Sdelphij		}
1282166255Sdelphij#endif
1283166255Sdelphij	}
1284166255Sdelphij
1285166255Sdelphij	if (cflag == 0) {
1286166255Sdelphij		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1287166255Sdelphij		if (out == -1) {
1288166255Sdelphij			maybe_warn("could not create output: %s", outfile);
1289166255Sdelphij			fclose(stdin);
1290209017Sdelphij			return (-1);
1291166255Sdelphij		}
1292207247Sdelphij#ifndef SMALL
1293207247Sdelphij		remove_file = outfile;
1294207247Sdelphij#endif
1295166255Sdelphij	} else
1296166255Sdelphij		out = STDOUT_FILENO;
1297166255Sdelphij
1298166255Sdelphij	insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1299166255Sdelphij
1300166255Sdelphij	(void)close(in);
1301166255Sdelphij
1302166255Sdelphij	/*
1303166255Sdelphij	 * If there was an error, insize will be -1.
1304166255Sdelphij	 * If we compressed to stdout, just return the size.
1305166255Sdelphij	 * Otherwise stat the file and check it is the correct size.
1306166255Sdelphij	 * We only blow away the file if we can stat the output and it
1307166255Sdelphij	 * has the expected size.
1308166255Sdelphij	 */
1309166255Sdelphij	if (cflag != 0)
1310209017Sdelphij		return (insize == -1 ? -1 : size);
1311166255Sdelphij
1312166255Sdelphij#ifndef SMALL
1313166255Sdelphij	if (fstat(out, &osb) != 0) {
1314166255Sdelphij		maybe_warn("couldn't stat: %s", outfile);
1315166255Sdelphij		goto bad_outfile;
1316166255Sdelphij	}
1317166255Sdelphij
1318166255Sdelphij	if (osb.st_size != size) {
1319209017Sdelphij		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1320209017Sdelphij		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1321166255Sdelphij		goto bad_outfile;
1322166255Sdelphij	}
1323166255Sdelphij
1324166255Sdelphij	copymodes(out, &isb, outfile);
1325207247Sdelphij	remove_file = NULL;
1326166255Sdelphij#endif
1327166255Sdelphij	if (close(out) == -1)
1328166255Sdelphij		maybe_warn("couldn't close output");
1329166255Sdelphij
1330166255Sdelphij	/* output is good, ok to delete input */
1331166255Sdelphij	unlink_input(file, &isb);
1332209017Sdelphij	return (size);
1333166255Sdelphij
1334166255Sdelphij#ifndef SMALL
1335166255Sdelphij    bad_outfile:
1336166255Sdelphij	if (close(out) == -1)
1337166255Sdelphij		maybe_warn("couldn't close output");
1338166255Sdelphij
1339166255Sdelphij	maybe_warnx("leaving original %s", file);
1340166255Sdelphij	unlink(outfile);
1341209017Sdelphij	return (size);
1342166255Sdelphij#endif
1343166255Sdelphij}
1344166255Sdelphij
1345166255Sdelphij/* uncompress the given file and remove the original */
1346166255Sdelphijstatic off_t
1347166255Sdelphijfile_uncompress(char *file, char *outfile, size_t outsize)
1348166255Sdelphij{
1349166255Sdelphij	struct stat isb, osb;
1350166255Sdelphij	off_t size;
1351166255Sdelphij	ssize_t rbytes;
1352166255Sdelphij	unsigned char header1[4];
1353166255Sdelphij	enum filetype method;
1354194508Sdelphij	int fd, ofd, zfd = -1;
1355166255Sdelphij#ifndef SMALL
1356206387Sdelphij	ssize_t rv;
1357166255Sdelphij	time_t timestamp = 0;
1358281500Sdelphij	char name[PATH_MAX + 1];
1359166255Sdelphij#endif
1360166255Sdelphij
1361166255Sdelphij	/* gather the old name info */
1362166255Sdelphij
1363166255Sdelphij	fd = open(file, O_RDONLY);
1364166255Sdelphij	if (fd < 0) {
1365166255Sdelphij		maybe_warn("can't open %s", file);
1366166255Sdelphij		goto lose;
1367166255Sdelphij	}
1368166255Sdelphij
1369166255Sdelphij	strlcpy(outfile, file, outsize);
1370166255Sdelphij	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1371166255Sdelphij		maybe_warnx("%s: unknown suffix -- ignored", file);
1372166255Sdelphij		goto lose;
1373166255Sdelphij	}
1374166255Sdelphij
1375166255Sdelphij	rbytes = read(fd, header1, sizeof header1);
1376166255Sdelphij	if (rbytes != sizeof header1) {
1377166255Sdelphij		/* we don't want to fail here. */
1378166255Sdelphij#ifndef SMALL
1379166255Sdelphij		if (fflag)
1380166255Sdelphij			goto lose;
1381166255Sdelphij#endif
1382166255Sdelphij		if (rbytes == -1)
1383166255Sdelphij			maybe_warn("can't read %s", file);
1384166255Sdelphij		else
1385166255Sdelphij			goto unexpected_EOF;
1386166255Sdelphij		goto lose;
1387166255Sdelphij	}
1388166255Sdelphij
1389166255Sdelphij	method = file_gettype(header1);
1390166255Sdelphij#ifndef SMALL
1391166255Sdelphij	if (fflag == 0 && method == FT_UNKNOWN) {
1392166255Sdelphij		maybe_warnx("%s: not in gzip format", file);
1393166255Sdelphij		goto lose;
1394166255Sdelphij	}
1395166255Sdelphij
1396166255Sdelphij#endif
1397166255Sdelphij
1398166255Sdelphij#ifndef SMALL
1399166255Sdelphij	if (method == FT_GZIP && Nflag) {
1400166255Sdelphij		unsigned char ts[4];	/* timestamp */
1401166255Sdelphij
1402166255Sdelphij		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1403194916Sdelphij		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1404166255Sdelphij			goto unexpected_EOF;
1405166255Sdelphij		if (rv == -1) {
1406166255Sdelphij			if (!fflag)
1407166255Sdelphij				maybe_warn("can't read %s", file);
1408166255Sdelphij			goto lose;
1409166255Sdelphij		}
1410166255Sdelphij		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1411166255Sdelphij
1412166255Sdelphij		if (header1[3] & ORIG_NAME) {
1413281540Sdelphij			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
1414166255Sdelphij			if (rbytes < 0) {
1415166255Sdelphij				maybe_warn("can't read %s", file);
1416166255Sdelphij				goto lose;
1417166255Sdelphij			}
1418281540Sdelphij			if (name[0] != '\0') {
1419281500Sdelphij				char *dp, *nf;
1420281500Sdelphij
1421281540Sdelphij				/* Make sure that name is NUL-terminated */
1422281540Sdelphij				name[rbytes] = '\0';
1423281540Sdelphij
1424281500Sdelphij				/* strip saved directory name */
1425281500Sdelphij				nf = strrchr(name, '/');
1426281500Sdelphij				if (nf == NULL)
1427281500Sdelphij					nf = name;
1428281500Sdelphij				else
1429281500Sdelphij					nf++;
1430281500Sdelphij
1431166255Sdelphij				/* preserve original directory name */
1432281500Sdelphij				dp = strrchr(file, '/');
1433166255Sdelphij				if (dp == NULL)
1434166255Sdelphij					dp = file;
1435166255Sdelphij				else
1436166255Sdelphij					dp++;
1437166255Sdelphij				snprintf(outfile, outsize, "%.*s%.*s",
1438166255Sdelphij						(int) (dp - file),
1439281500Sdelphij						file, (int) rbytes, nf);
1440166255Sdelphij			}
1441166255Sdelphij		}
1442166255Sdelphij	}
1443166255Sdelphij#endif
1444166255Sdelphij	lseek(fd, 0, SEEK_SET);
1445166255Sdelphij
1446166255Sdelphij	if (cflag == 0 || lflag) {
1447166255Sdelphij		if (fstat(fd, &isb) != 0)
1448166255Sdelphij			goto lose;
1449166255Sdelphij#ifndef SMALL
1450166255Sdelphij		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1451166255Sdelphij			maybe_warnx("%s has %d other links -- skipping",
1452166255Sdelphij			    file, isb.st_nlink - 1);
1453166255Sdelphij			goto lose;
1454166255Sdelphij		}
1455166255Sdelphij		if (nflag == 0 && timestamp)
1456166255Sdelphij			isb.st_mtime = timestamp;
1457166255Sdelphij		if (check_outfile(outfile) == 0)
1458166255Sdelphij			goto lose;
1459166255Sdelphij#endif
1460166255Sdelphij	}
1461166255Sdelphij
1462166255Sdelphij	if (cflag == 0 && lflag == 0) {
1463166255Sdelphij		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1464166255Sdelphij		if (zfd == STDOUT_FILENO) {
1465166255Sdelphij			/* We won't close STDOUT_FILENO later... */
1466166255Sdelphij			zfd = dup(zfd);
1467166255Sdelphij			close(STDOUT_FILENO);
1468166255Sdelphij		}
1469166255Sdelphij		if (zfd == -1) {
1470166255Sdelphij			maybe_warn("can't open %s", outfile);
1471166255Sdelphij			goto lose;
1472166255Sdelphij		}
1473207247Sdelphij#ifndef SMALL
1474207247Sdelphij		remove_file = outfile;
1475207247Sdelphij#endif
1476166255Sdelphij	} else
1477166255Sdelphij		zfd = STDOUT_FILENO;
1478166255Sdelphij
1479226184Sdelphij	switch (method) {
1480166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1481226184Sdelphij	case FT_BZIP2:
1482166255Sdelphij		/* XXX */
1483166255Sdelphij		if (lflag) {
1484166255Sdelphij			maybe_warnx("no -l with bzip2 files");
1485166255Sdelphij			goto lose;
1486166255Sdelphij		}
1487166255Sdelphij
1488166255Sdelphij		size = unbzip2(fd, zfd, NULL, 0, NULL);
1489226184Sdelphij		break;
1490166255Sdelphij#endif
1491166255Sdelphij
1492166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1493226184Sdelphij	case FT_Z: {
1494166255Sdelphij		FILE *in, *out;
1495166255Sdelphij
1496166255Sdelphij		/* XXX */
1497166255Sdelphij		if (lflag) {
1498166255Sdelphij			maybe_warnx("no -l with Lempel-Ziv files");
1499166255Sdelphij			goto lose;
1500166255Sdelphij		}
1501166255Sdelphij
1502166255Sdelphij		if ((in = zdopen(fd)) == NULL) {
1503166255Sdelphij			maybe_warn("zdopen for read: %s", file);
1504166255Sdelphij			goto lose;
1505166255Sdelphij		}
1506166255Sdelphij
1507166255Sdelphij		out = fdopen(dup(zfd), "w");
1508166255Sdelphij		if (out == NULL) {
1509166255Sdelphij			maybe_warn("fdopen for write: %s", outfile);
1510166255Sdelphij			fclose(in);
1511166255Sdelphij			goto lose;
1512166255Sdelphij		}
1513166255Sdelphij
1514166255Sdelphij		size = zuncompress(in, out, NULL, 0, NULL);
1515166255Sdelphij		/* need to fclose() if ferror() is true... */
1516166255Sdelphij		if (ferror(in) | fclose(in)) {
1517166255Sdelphij			maybe_warn("failed infile fclose");
1518166255Sdelphij			unlink(outfile);
1519166255Sdelphij			(void)fclose(out);
1520166255Sdelphij		}
1521166255Sdelphij		if (fclose(out) != 0) {
1522166255Sdelphij			maybe_warn("failed outfile fclose");
1523166255Sdelphij			unlink(outfile);
1524166255Sdelphij			goto lose;
1525166255Sdelphij		}
1526226184Sdelphij		break;
1527226184Sdelphij	}
1528166255Sdelphij#endif
1529166255Sdelphij
1530194579Sdelphij#ifndef NO_PACK_SUPPORT
1531226184Sdelphij	case FT_PACK:
1532194579Sdelphij		if (lflag) {
1533194579Sdelphij			maybe_warnx("no -l with packed files");
1534194579Sdelphij			goto lose;
1535194579Sdelphij		}
1536194579Sdelphij
1537194579Sdelphij		size = unpack(fd, zfd, NULL, 0, NULL);
1538226184Sdelphij		break;
1539194579Sdelphij#endif
1540194579Sdelphij
1541226184Sdelphij#ifndef NO_XZ_SUPPORT
1542226184Sdelphij	case FT_XZ:
1543226184Sdelphij		if (lflag) {
1544226184Sdelphij			maybe_warnx("no -l with xz files");
1545226184Sdelphij			goto lose;
1546226184Sdelphij		}
1547226184Sdelphij
1548226184Sdelphij		size = unxz(fd, zfd, NULL, 0, NULL);
1549226184Sdelphij		break;
1550226184Sdelphij#endif
1551226184Sdelphij
1552166255Sdelphij#ifndef SMALL
1553226184Sdelphij	case FT_UNKNOWN:
1554166255Sdelphij		if (lflag) {
1555166255Sdelphij			maybe_warnx("no -l for unknown filetypes");
1556166255Sdelphij			goto lose;
1557166255Sdelphij		}
1558166255Sdelphij		size = cat_fd(NULL, 0, NULL, fd);
1559226184Sdelphij		break;
1560166255Sdelphij#endif
1561226184Sdelphij	default:
1562166255Sdelphij		if (lflag) {
1563166255Sdelphij			print_list(fd, isb.st_size, outfile, isb.st_mtime);
1564166255Sdelphij			close(fd);
1565166255Sdelphij			return -1;	/* XXX */
1566166255Sdelphij		}
1567166255Sdelphij
1568166255Sdelphij		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1569226184Sdelphij		break;
1570166255Sdelphij	}
1571166255Sdelphij
1572166255Sdelphij	if (close(fd) != 0)
1573166255Sdelphij		maybe_warn("couldn't close input");
1574166255Sdelphij	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1575166255Sdelphij		maybe_warn("couldn't close output");
1576166255Sdelphij
1577166255Sdelphij	if (size == -1) {
1578166255Sdelphij		if (cflag == 0)
1579166255Sdelphij			unlink(outfile);
1580166255Sdelphij		maybe_warnx("%s: uncompress failed", file);
1581166255Sdelphij		return -1;
1582166255Sdelphij	}
1583166255Sdelphij
1584166255Sdelphij	/* if testing, or we uncompressed to stdout, this is all we need */
1585166255Sdelphij#ifndef SMALL
1586166255Sdelphij	if (tflag)
1587166255Sdelphij		return size;
1588166255Sdelphij#endif
1589166255Sdelphij	/* if we are uncompressing to stdin, don't remove the file. */
1590166255Sdelphij	if (cflag)
1591166255Sdelphij		return size;
1592166255Sdelphij
1593166255Sdelphij	/*
1594166255Sdelphij	 * if we create a file...
1595166255Sdelphij	 */
1596166255Sdelphij	/*
1597166255Sdelphij	 * if we can't stat the file don't remove the file.
1598166255Sdelphij	 */
1599166255Sdelphij
1600166255Sdelphij	ofd = open(outfile, O_RDWR, 0);
1601166255Sdelphij	if (ofd == -1) {
1602166255Sdelphij		maybe_warn("couldn't open (leaving original): %s",
1603166255Sdelphij			   outfile);
1604166255Sdelphij		return -1;
1605166255Sdelphij	}
1606166255Sdelphij	if (fstat(ofd, &osb) != 0) {
1607166255Sdelphij		maybe_warn("couldn't stat (leaving original): %s",
1608166255Sdelphij			   outfile);
1609166255Sdelphij		close(ofd);
1610166255Sdelphij		return -1;
1611166255Sdelphij	}
1612166255Sdelphij	if (osb.st_size != size) {
1613209017Sdelphij		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1614209017Sdelphij		    (uintmax_t)size, (uintmax_t)osb.st_size);
1615166255Sdelphij		close(ofd);
1616166255Sdelphij		unlink(outfile);
1617166255Sdelphij		return -1;
1618166255Sdelphij	}
1619166255Sdelphij#ifndef SMALL
1620166255Sdelphij	copymodes(ofd, &isb, outfile);
1621207247Sdelphij	remove_file = NULL;
1622166255Sdelphij#endif
1623166255Sdelphij	close(ofd);
1624207247Sdelphij	unlink_input(file, &isb);
1625166255Sdelphij	return size;
1626166255Sdelphij
1627166255Sdelphij    unexpected_EOF:
1628166255Sdelphij	maybe_warnx("%s: unexpected end of file", file);
1629166255Sdelphij    lose:
1630166255Sdelphij	if (fd != -1)
1631166255Sdelphij		close(fd);
1632166255Sdelphij	if (zfd != -1 && zfd != STDOUT_FILENO)
1633166255Sdelphij		close(fd);
1634166255Sdelphij	return -1;
1635166255Sdelphij}
1636166255Sdelphij
1637166255Sdelphij#ifndef SMALL
1638166255Sdelphijstatic off_t
1639166255Sdelphijcat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1640166255Sdelphij{
1641166255Sdelphij	char buf[BUFLEN];
1642166255Sdelphij	off_t in_tot;
1643166255Sdelphij	ssize_t w;
1644166255Sdelphij
1645166255Sdelphij	in_tot = count;
1646166255Sdelphij	w = write(STDOUT_FILENO, prepend, count);
1647166255Sdelphij	if (w == -1 || (size_t)w != count) {
1648166255Sdelphij		maybe_warn("write to stdout");
1649166255Sdelphij		return -1;
1650166255Sdelphij	}
1651166255Sdelphij	for (;;) {
1652166255Sdelphij		ssize_t rv;
1653166255Sdelphij
1654166255Sdelphij		rv = read(fd, buf, sizeof buf);
1655166255Sdelphij		if (rv == 0)
1656166255Sdelphij			break;
1657166255Sdelphij		if (rv < 0) {
1658166255Sdelphij			maybe_warn("read from fd %d", fd);
1659166255Sdelphij			break;
1660166255Sdelphij		}
1661166255Sdelphij
1662166255Sdelphij		if (write(STDOUT_FILENO, buf, rv) != rv) {
1663166255Sdelphij			maybe_warn("write to stdout");
1664166255Sdelphij			break;
1665166255Sdelphij		}
1666166255Sdelphij		in_tot += rv;
1667166255Sdelphij	}
1668166255Sdelphij
1669166255Sdelphij	if (gsizep)
1670166255Sdelphij		*gsizep = in_tot;
1671166255Sdelphij	return (in_tot);
1672166255Sdelphij}
1673166255Sdelphij#endif
1674166255Sdelphij
1675166255Sdelphijstatic void
1676166255Sdelphijhandle_stdin(void)
1677166255Sdelphij{
1678166255Sdelphij	unsigned char header1[4];
1679166255Sdelphij	off_t usize, gsize;
1680166255Sdelphij	enum filetype method;
1681166255Sdelphij	ssize_t bytes_read;
1682166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1683166255Sdelphij	FILE *in;
1684166255Sdelphij#endif
1685166255Sdelphij
1686166255Sdelphij#ifndef SMALL
1687166255Sdelphij	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1688166255Sdelphij		maybe_warnx("standard input is a terminal -- ignoring");
1689166255Sdelphij		return;
1690166255Sdelphij	}
1691166255Sdelphij#endif
1692166255Sdelphij
1693166255Sdelphij	if (lflag) {
1694166255Sdelphij		struct stat isb;
1695166255Sdelphij
1696166255Sdelphij		/* XXX could read the whole file, etc. */
1697166255Sdelphij		if (fstat(STDIN_FILENO, &isb) < 0) {
1698166255Sdelphij			maybe_warn("fstat");
1699166255Sdelphij			return;
1700166255Sdelphij		}
1701166255Sdelphij		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1702166255Sdelphij		return;
1703166255Sdelphij	}
1704166255Sdelphij
1705166255Sdelphij	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1706166255Sdelphij	if (bytes_read == -1) {
1707166255Sdelphij		maybe_warn("can't read stdin");
1708166255Sdelphij		return;
1709166255Sdelphij	} else if (bytes_read != sizeof(header1)) {
1710166255Sdelphij		maybe_warnx("(stdin): unexpected end of file");
1711166255Sdelphij		return;
1712166255Sdelphij	}
1713166255Sdelphij
1714166255Sdelphij	method = file_gettype(header1);
1715166255Sdelphij	switch (method) {
1716166255Sdelphij	default:
1717166255Sdelphij#ifndef SMALL
1718166255Sdelphij		if (fflag == 0) {
1719166255Sdelphij			maybe_warnx("unknown compression format");
1720166255Sdelphij			return;
1721166255Sdelphij		}
1722166255Sdelphij		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1723166255Sdelphij		break;
1724166255Sdelphij#endif
1725166255Sdelphij	case FT_GZIP:
1726166255Sdelphij		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1727166255Sdelphij			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1728166255Sdelphij		break;
1729166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1730166255Sdelphij	case FT_BZIP2:
1731166255Sdelphij		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1732166255Sdelphij				(char *)header1, sizeof header1, &gsize);
1733166255Sdelphij		break;
1734166255Sdelphij#endif
1735166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1736166255Sdelphij	case FT_Z:
1737166255Sdelphij		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1738166255Sdelphij			maybe_warnx("zopen of stdin");
1739166255Sdelphij			return;
1740166255Sdelphij		}
1741166255Sdelphij
1742226184Sdelphij		usize = zuncompress(in, stdout, (char *)header1,
1743226184Sdelphij		    sizeof header1, &gsize);
1744166255Sdelphij		fclose(in);
1745166255Sdelphij		break;
1746166255Sdelphij#endif
1747194579Sdelphij#ifndef NO_PACK_SUPPORT
1748194579Sdelphij	case FT_PACK:
1749194579Sdelphij		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1750194579Sdelphij			       (char *)header1, sizeof header1, &gsize);
1751194579Sdelphij		break;
1752194579Sdelphij#endif
1753226184Sdelphij#ifndef NO_XZ_SUPPORT
1754226184Sdelphij	case FT_XZ:
1755226184Sdelphij		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1756226184Sdelphij			     (char *)header1, sizeof header1, &gsize);
1757226184Sdelphij		break;
1758226184Sdelphij#endif
1759166255Sdelphij	}
1760166255Sdelphij
1761166255Sdelphij#ifndef SMALL
1762166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1763166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1764166255Sdelphij	if (vflag && tflag)
1765166255Sdelphij		print_test("(stdin)", usize != -1);
1766166255Sdelphij#endif
1767166255Sdelphij
1768166255Sdelphij}
1769166255Sdelphij
1770166255Sdelphijstatic void
1771166255Sdelphijhandle_stdout(void)
1772166255Sdelphij{
1773166255Sdelphij	off_t gsize, usize;
1774166255Sdelphij	struct stat sb;
1775166255Sdelphij	time_t systime;
1776166255Sdelphij	uint32_t mtime;
1777166255Sdelphij	int ret;
1778166255Sdelphij
1779166255Sdelphij#ifndef SMALL
1780166255Sdelphij	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1781166255Sdelphij		maybe_warnx("standard output is a terminal -- ignoring");
1782166255Sdelphij		return;
1783166255Sdelphij	}
1784166255Sdelphij#endif
1785273507Sdelphij	/* If stdin is a file use its mtime, otherwise use current time */
1786166255Sdelphij	ret = fstat(STDIN_FILENO, &sb);
1787166255Sdelphij
1788166255Sdelphij#ifndef SMALL
1789166255Sdelphij	if (ret < 0) {
1790166255Sdelphij		maybe_warn("Can't stat stdin");
1791166255Sdelphij		return;
1792166255Sdelphij	}
1793166255Sdelphij#endif
1794166255Sdelphij
1795166255Sdelphij	if (S_ISREG(sb.st_mode))
1796166255Sdelphij		mtime = (uint32_t)sb.st_mtime;
1797166255Sdelphij	else {
1798166255Sdelphij		systime = time(NULL);
1799166255Sdelphij#ifndef SMALL
1800166255Sdelphij		if (systime == -1) {
1801166255Sdelphij			maybe_warn("time");
1802166255Sdelphij			return;
1803166255Sdelphij		}
1804166255Sdelphij#endif
1805166255Sdelphij		mtime = (uint32_t)systime;
1806166255Sdelphij	}
1807166255Sdelphij
1808166255Sdelphij	usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1809166255Sdelphij#ifndef SMALL
1810166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1811166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1812166255Sdelphij#endif
1813166255Sdelphij}
1814166255Sdelphij
1815166255Sdelphij/* do what is asked for, for the path name */
1816166255Sdelphijstatic void
1817166255Sdelphijhandle_pathname(char *path)
1818166255Sdelphij{
1819166255Sdelphij	char *opath = path, *s = NULL;
1820166255Sdelphij	ssize_t len;
1821166255Sdelphij	int slen;
1822166255Sdelphij	struct stat sb;
1823166255Sdelphij
1824166255Sdelphij	/* check for stdout/stdin */
1825166255Sdelphij	if (path[0] == '-' && path[1] == '\0') {
1826166255Sdelphij		if (dflag)
1827166255Sdelphij			handle_stdin();
1828166255Sdelphij		else
1829166255Sdelphij			handle_stdout();
1830166255Sdelphij		return;
1831166255Sdelphij	}
1832166255Sdelphij
1833166255Sdelphijretry:
1834222287Sdelphij	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1835222287Sdelphij	    lstat(path, &sb) != 0)) {
1836166255Sdelphij		/* lets try <path>.gz if we're decompressing */
1837166255Sdelphij		if (dflag && s == NULL && errno == ENOENT) {
1838166255Sdelphij			len = strlen(path);
1839166255Sdelphij			slen = suffixes[0].ziplen;
1840166255Sdelphij			s = malloc(len + slen + 1);
1841166255Sdelphij			if (s == NULL)
1842166255Sdelphij				maybe_err("malloc");
1843166255Sdelphij			memcpy(s, path, len);
1844166255Sdelphij			memcpy(s + len, suffixes[0].zipped, slen + 1);
1845166255Sdelphij			path = s;
1846166255Sdelphij			goto retry;
1847166255Sdelphij		}
1848166255Sdelphij		maybe_warn("can't stat: %s", opath);
1849166255Sdelphij		goto out;
1850166255Sdelphij	}
1851166255Sdelphij
1852166255Sdelphij	if (S_ISDIR(sb.st_mode)) {
1853166255Sdelphij#ifndef SMALL
1854166255Sdelphij		if (rflag)
1855166255Sdelphij			handle_dir(path);
1856166255Sdelphij		else
1857166255Sdelphij#endif
1858166255Sdelphij			maybe_warnx("%s is a directory", path);
1859166255Sdelphij		goto out;
1860166255Sdelphij	}
1861166255Sdelphij
1862166255Sdelphij	if (S_ISREG(sb.st_mode))
1863166255Sdelphij		handle_file(path, &sb);
1864166255Sdelphij	else
1865166255Sdelphij		maybe_warnx("%s is not a regular file", path);
1866166255Sdelphij
1867166255Sdelphijout:
1868166255Sdelphij	if (s)
1869166255Sdelphij		free(s);
1870166255Sdelphij}
1871166255Sdelphij
1872166255Sdelphij/* compress/decompress a file */
1873166255Sdelphijstatic void
1874166255Sdelphijhandle_file(char *file, struct stat *sbp)
1875166255Sdelphij{
1876166255Sdelphij	off_t usize, gsize;
1877166255Sdelphij	char	outfile[PATH_MAX];
1878166255Sdelphij
1879166255Sdelphij	infile = file;
1880166255Sdelphij	if (dflag) {
1881166255Sdelphij		usize = file_uncompress(file, outfile, sizeof(outfile));
1882166255Sdelphij#ifndef SMALL
1883166255Sdelphij		if (vflag && tflag)
1884166255Sdelphij			print_test(file, usize != -1);
1885166255Sdelphij#endif
1886166255Sdelphij		if (usize == -1)
1887166255Sdelphij			return;
1888166255Sdelphij		gsize = sbp->st_size;
1889166255Sdelphij	} else {
1890166255Sdelphij		gsize = file_compress(file, outfile, sizeof(outfile));
1891166255Sdelphij		if (gsize == -1)
1892166255Sdelphij			return;
1893166255Sdelphij		usize = sbp->st_size;
1894166255Sdelphij	}
1895166255Sdelphij
1896166255Sdelphij
1897166255Sdelphij#ifndef SMALL
1898166255Sdelphij	if (vflag && !tflag)
1899166255Sdelphij		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1900166255Sdelphij#endif
1901166255Sdelphij}
1902166255Sdelphij
1903166255Sdelphij#ifndef SMALL
1904166255Sdelphij/* this is used with -r to recursively descend directories */
1905166255Sdelphijstatic void
1906166255Sdelphijhandle_dir(char *dir)
1907166255Sdelphij{
1908166255Sdelphij	char *path_argv[2];
1909166255Sdelphij	FTS *fts;
1910166255Sdelphij	FTSENT *entry;
1911166255Sdelphij
1912166255Sdelphij	path_argv[0] = dir;
1913166255Sdelphij	path_argv[1] = 0;
1914171389Sdelphij	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1915166255Sdelphij	if (fts == NULL) {
1916166255Sdelphij		warn("couldn't fts_open %s", dir);
1917166255Sdelphij		return;
1918166255Sdelphij	}
1919166255Sdelphij
1920166255Sdelphij	while ((entry = fts_read(fts))) {
1921166255Sdelphij		switch(entry->fts_info) {
1922166255Sdelphij		case FTS_D:
1923166255Sdelphij		case FTS_DP:
1924166255Sdelphij			continue;
1925166255Sdelphij
1926166255Sdelphij		case FTS_DNR:
1927166255Sdelphij		case FTS_ERR:
1928166255Sdelphij		case FTS_NS:
1929166255Sdelphij			maybe_warn("%s", entry->fts_path);
1930166255Sdelphij			continue;
1931166255Sdelphij		case FTS_F:
1932171389Sdelphij			handle_file(entry->fts_path, entry->fts_statp);
1933166255Sdelphij		}
1934166255Sdelphij	}
1935166255Sdelphij	(void)fts_close(fts);
1936166255Sdelphij}
1937166255Sdelphij#endif
1938166255Sdelphij
1939166255Sdelphij/* print a ratio - size reduction as a fraction of uncompressed size */
1940166255Sdelphijstatic void
1941166255Sdelphijprint_ratio(off_t in, off_t out, FILE *where)
1942166255Sdelphij{
1943166255Sdelphij	int percent10;	/* 10 * percent */
1944166255Sdelphij	off_t diff;
1945166255Sdelphij	char buff[8];
1946166255Sdelphij	int len;
1947166255Sdelphij
1948166255Sdelphij	diff = in - out/2;
1949166255Sdelphij	if (diff <= 0)
1950166255Sdelphij		/*
1951166255Sdelphij		 * Output is more than double size of input! print -99.9%
1952166255Sdelphij		 * Quite possibly we've failed to get the original size.
1953166255Sdelphij		 */
1954166255Sdelphij		percent10 = -999;
1955166255Sdelphij	else {
1956166255Sdelphij		/*
1957166255Sdelphij		 * We only need 12 bits of result from the final division,
1958166255Sdelphij		 * so reduce the values until a 32bit division will suffice.
1959166255Sdelphij		 */
1960166255Sdelphij		while (in > 0x100000) {
1961166255Sdelphij			diff >>= 1;
1962166255Sdelphij			in >>= 1;
1963166255Sdelphij		}
1964166255Sdelphij		if (in != 0)
1965166255Sdelphij			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1966166255Sdelphij		else
1967166255Sdelphij			percent10 = 0;
1968166255Sdelphij	}
1969166255Sdelphij
1970166255Sdelphij	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1971166255Sdelphij	/* Move the '.' to before the last digit */
1972166255Sdelphij	buff[len - 1] = buff[len - 2];
1973166255Sdelphij	buff[len - 2] = '.';
1974166255Sdelphij	fprintf(where, "%5s%%", buff);
1975166255Sdelphij}
1976166255Sdelphij
1977166255Sdelphij#ifndef SMALL
1978166255Sdelphij/* print compression statistics, and the new name (if there is one!) */
1979166255Sdelphijstatic void
1980166255Sdelphijprint_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1981166255Sdelphij{
1982166255Sdelphij	if (file)
1983166255Sdelphij		fprintf(stderr, "%s:%s  ", file,
1984166255Sdelphij		    strlen(file) < 7 ? "\t\t" : "\t");
1985166255Sdelphij	print_ratio(usize, gsize, stderr);
1986166255Sdelphij	if (nfile)
1987166255Sdelphij		fprintf(stderr, " -- replaced with %s", nfile);
1988166255Sdelphij	fprintf(stderr, "\n");
1989166255Sdelphij	fflush(stderr);
1990166255Sdelphij}
1991166255Sdelphij
1992166255Sdelphij/* print test results */
1993166255Sdelphijstatic void
1994166255Sdelphijprint_test(const char *file, int ok)
1995166255Sdelphij{
1996166255Sdelphij
1997166255Sdelphij	if (exit_value == 0 && ok == 0)
1998166255Sdelphij		exit_value = 1;
1999166255Sdelphij	fprintf(stderr, "%s:%s  %s\n", file,
2000166255Sdelphij	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2001166255Sdelphij	fflush(stderr);
2002166255Sdelphij}
2003166255Sdelphij#endif
2004166255Sdelphij
2005166255Sdelphij/* print a file's info ala --list */
2006166255Sdelphij/* eg:
2007166255Sdelphij  compressed uncompressed  ratio uncompressed_name
2008166255Sdelphij      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2009166255Sdelphij*/
2010166255Sdelphijstatic void
2011166255Sdelphijprint_list(int fd, off_t out, const char *outfile, time_t ts)
2012166255Sdelphij{
2013166255Sdelphij	static int first = 1;
2014166255Sdelphij#ifndef SMALL
2015166255Sdelphij	static off_t in_tot, out_tot;
2016166255Sdelphij	uint32_t crc = 0;
2017166255Sdelphij#endif
2018166255Sdelphij	off_t in = 0, rv;
2019166255Sdelphij
2020166255Sdelphij	if (first) {
2021166255Sdelphij#ifndef SMALL
2022166255Sdelphij		if (vflag)
2023166255Sdelphij			printf("method  crc     date  time  ");
2024166255Sdelphij#endif
2025166255Sdelphij		if (qflag == 0)
2026166255Sdelphij			printf("  compressed uncompressed  "
2027166255Sdelphij			       "ratio uncompressed_name\n");
2028166255Sdelphij	}
2029166255Sdelphij	first = 0;
2030166255Sdelphij
2031166255Sdelphij	/* print totals? */
2032166255Sdelphij#ifndef SMALL
2033166255Sdelphij	if (fd == -1) {
2034166255Sdelphij		in = in_tot;
2035166255Sdelphij		out = out_tot;
2036166255Sdelphij	} else
2037166255Sdelphij#endif
2038166255Sdelphij	{
2039166255Sdelphij		/* read the last 4 bytes - this is the uncompressed size */
2040166255Sdelphij		rv = lseek(fd, (off_t)(-8), SEEK_END);
2041166255Sdelphij		if (rv != -1) {
2042166255Sdelphij			unsigned char buf[8];
2043166255Sdelphij			uint32_t usize;
2044166255Sdelphij
2045166255Sdelphij			rv = read(fd, (char *)buf, sizeof(buf));
2046166255Sdelphij			if (rv == -1)
2047166255Sdelphij				maybe_warn("read of uncompressed size");
2048166255Sdelphij			else if (rv != sizeof(buf))
2049166255Sdelphij				maybe_warnx("read of uncompressed size");
2050166255Sdelphij
2051166255Sdelphij			else {
2052166255Sdelphij				usize = buf[4] | buf[5] << 8 |
2053166255Sdelphij					buf[6] << 16 | buf[7] << 24;
2054166255Sdelphij				in = (off_t)usize;
2055166255Sdelphij#ifndef SMALL
2056166255Sdelphij				crc = buf[0] | buf[1] << 8 |
2057166255Sdelphij				      buf[2] << 16 | buf[3] << 24;
2058166255Sdelphij#endif
2059166255Sdelphij			}
2060166255Sdelphij		}
2061166255Sdelphij	}
2062166255Sdelphij
2063166255Sdelphij#ifndef SMALL
2064166255Sdelphij	if (vflag && fd == -1)
2065166255Sdelphij		printf("                            ");
2066166255Sdelphij	else if (vflag) {
2067166255Sdelphij		char *date = ctime(&ts);
2068166255Sdelphij
2069166255Sdelphij		/* skip the day, 1/100th second, and year */
2070166255Sdelphij		date += 4;
2071166255Sdelphij		date[12] = 0;
2072166255Sdelphij		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2073166255Sdelphij	}
2074166255Sdelphij	in_tot += in;
2075166255Sdelphij	out_tot += out;
2076194508Sdelphij#else
2077194508Sdelphij	(void)&ts;	/* XXX */
2078166255Sdelphij#endif
2079166255Sdelphij	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2080166255Sdelphij	print_ratio(in, out, stdout);
2081166255Sdelphij	printf(" %s\n", outfile);
2082166255Sdelphij}
2083166255Sdelphij
2084166255Sdelphij/* display the usage of NetBSD gzip */
2085166255Sdelphijstatic void
2086166255Sdelphijusage(void)
2087166255Sdelphij{
2088166255Sdelphij
2089166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2090166255Sdelphij	fprintf(stderr,
2091171389Sdelphij#ifdef SMALL
2092171389Sdelphij    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2093171389Sdelphij#else
2094171389Sdelphij    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2095166255Sdelphij    " -1 --fast            fastest (worst) compression\n"
2096166255Sdelphij    " -2 .. -8             set compression level\n"
2097166255Sdelphij    " -9 --best            best (slowest) compression\n"
2098166255Sdelphij    " -c --stdout          write to stdout, keep original files\n"
2099166255Sdelphij    "    --to-stdout\n"
2100166255Sdelphij    " -d --decompress      uncompress files\n"
2101166255Sdelphij    "    --uncompress\n"
2102166255Sdelphij    " -f --force           force overwriting & compress links\n"
2103166255Sdelphij    " -h --help            display this help\n"
2104170053Sdelphij    " -k --keep            don't delete input files during operation\n"
2105166255Sdelphij    " -l --list            list compressed file contents\n"
2106166255Sdelphij    " -N --name            save or restore original file name and time stamp\n"
2107166255Sdelphij    " -n --no-name         don't save original file name or time stamp\n"
2108166255Sdelphij    " -q --quiet           output no warnings\n"
2109166255Sdelphij    " -r --recursive       recursively compress files in directories\n"
2110166255Sdelphij    " -S .suf              use suffix .suf instead of .gz\n"
2111166255Sdelphij    "    --suffix .suf\n"
2112166255Sdelphij    " -t --test            test compressed file\n"
2113166255Sdelphij    " -V --version         display program version\n"
2114166255Sdelphij    " -v --verbose         print extra statistics\n",
2115166255Sdelphij#endif
2116166255Sdelphij	    getprogname());
2117166255Sdelphij	exit(0);
2118166255Sdelphij}
2119166255Sdelphij
2120166255Sdelphij#ifndef SMALL
2121166255Sdelphij/* display the license information of FreeBSD gzip */
2122166255Sdelphijstatic void
2123166255Sdelphijdisplay_license(void)
2124166255Sdelphij{
2125166255Sdelphij
2126281500Sdelphij	fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
2127166255Sdelphij	fprintf(stderr, "%s\n", gzip_copyright);
2128166255Sdelphij	exit(0);
2129166255Sdelphij}
2130166255Sdelphij#endif
2131166255Sdelphij
2132166255Sdelphij/* display the version of NetBSD gzip */
2133166255Sdelphijstatic void
2134166255Sdelphijdisplay_version(void)
2135166255Sdelphij{
2136166255Sdelphij
2137166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2138166255Sdelphij	exit(0);
2139166255Sdelphij}
2140166255Sdelphij
2141166255Sdelphij#ifndef NO_BZIP2_SUPPORT
2142166255Sdelphij#include "unbzip2.c"
2143166255Sdelphij#endif
2144166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
2145166255Sdelphij#include "zuncompress.c"
2146166255Sdelphij#endif
2147194579Sdelphij#ifndef NO_PACK_SUPPORT
2148194579Sdelphij#include "unpack.c"
2149194579Sdelphij#endif
2150226184Sdelphij#ifndef NO_XZ_SUPPORT
2151226184Sdelphij#include "unxz.c"
2152226184Sdelphij#endif
2153166255Sdelphij
2154166255Sdelphijstatic ssize_t
2155166255Sdelphijread_retry(int fd, void *buf, size_t sz)
2156166255Sdelphij{
2157166255Sdelphij	char *cp = buf;
2158166255Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2159166255Sdelphij
2160166255Sdelphij	while (left > 0) {
2161166255Sdelphij		ssize_t ret;
2162166255Sdelphij
2163166255Sdelphij		ret = read(fd, cp, left);
2164166255Sdelphij		if (ret == -1) {
2165166255Sdelphij			return ret;
2166166255Sdelphij		} else if (ret == 0) {
2167166255Sdelphij			break; /* EOF */
2168166255Sdelphij		}
2169166255Sdelphij		cp += ret;
2170166255Sdelphij		left -= ret;
2171166255Sdelphij	}
2172166255Sdelphij
2173166255Sdelphij	return sz - left;
2174166255Sdelphij}
2175