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