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