gzip.c revision 337521
1336323Spfg/*	$NetBSD: gzip.c,v 1.113 2018/06/12 00:42:17 kamil Exp $	*/
2166255Sdelphij
3166255Sdelphij/*-
4330449Seadler * SPDX-License-Identifier: BSD-2-Clause-NetBSD
5330449Seadler *
6326559Sdelphij * Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008, 2009, 2010, 2011, 2015, 2017
7326559Sdelphij *    Matthew R. Green
8166255Sdelphij * All rights reserved.
9166255Sdelphij *
10166255Sdelphij * Redistribution and use in source and binary forms, with or without
11166255Sdelphij * modification, are permitted provided that the following conditions
12166255Sdelphij * are met:
13166255Sdelphij * 1. Redistributions of source code must retain the above copyright
14166255Sdelphij *    notice, this list of conditions and the following disclaimer.
15166255Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
16166255Sdelphij *    notice, this list of conditions and the following disclaimer in the
17166255Sdelphij *    documentation and/or other materials provided with the distribution.
18166255Sdelphij *
19166255Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20166255Sdelphij * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21166255Sdelphij * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22166255Sdelphij * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23166255Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24166255Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25166255Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26166255Sdelphij * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27166255Sdelphij * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28166255Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29166255Sdelphij * SUCH DAMAGE.
30166255Sdelphij *
31166255Sdelphij */
32166255Sdelphij
33166255Sdelphij#include <sys/cdefs.h>
34166255Sdelphij#ifndef lint
35326559Sdelphij__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008,\
36326559Sdelphij 2009, 2010, 2011, 2015, 2017 Matthew R. Green.  All rights reserved.");
37222210Sdelphij__FBSDID("$FreeBSD: stable/11/usr.bin/gzip/gzip.c 337521 2018-08-09 02:27:18Z delphij $");
38166255Sdelphij#endif /* not lint */
39166255Sdelphij
40166255Sdelphij/*
41166255Sdelphij * gzip.c -- GPL free gzip using zlib.
42166255Sdelphij *
43166255Sdelphij * RFC 1950 covers the zlib format
44166255Sdelphij * RFC 1951 covers the deflate format
45166255Sdelphij * RFC 1952 covers the gzip format
46166255Sdelphij *
47166255Sdelphij * TODO:
48166255Sdelphij *	- use mmap where possible
49166255Sdelphij *	- make bzip2/compress -v/-t/-l support work as well as possible
50166255Sdelphij */
51166255Sdelphij
52336661Sdelphij#include <sys/endian.h>
53166255Sdelphij#include <sys/param.h>
54166255Sdelphij#include <sys/stat.h>
55166255Sdelphij#include <sys/time.h>
56166255Sdelphij
57166255Sdelphij#include <inttypes.h>
58166255Sdelphij#include <unistd.h>
59166255Sdelphij#include <stdio.h>
60166255Sdelphij#include <string.h>
61166255Sdelphij#include <stdlib.h>
62166255Sdelphij#include <err.h>
63166255Sdelphij#include <errno.h>
64166255Sdelphij#include <fcntl.h>
65166255Sdelphij#include <zlib.h>
66166255Sdelphij#include <fts.h>
67166255Sdelphij#include <libgen.h>
68166255Sdelphij#include <stdarg.h>
69166255Sdelphij#include <getopt.h>
70166255Sdelphij#include <time.h>
71166255Sdelphij
72166255Sdelphij/* what type of file are we dealing with */
73166255Sdelphijenum filetype {
74166255Sdelphij	FT_GZIP,
75166255Sdelphij#ifndef NO_BZIP2_SUPPORT
76166255Sdelphij	FT_BZIP2,
77166255Sdelphij#endif
78166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
79166255Sdelphij	FT_Z,
80166255Sdelphij#endif
81194579Sdelphij#ifndef NO_PACK_SUPPORT
82194579Sdelphij	FT_PACK,
83194579Sdelphij#endif
84226184Sdelphij#ifndef NO_XZ_SUPPORT
85226184Sdelphij	FT_XZ,
86226184Sdelphij#endif
87166255Sdelphij	FT_LAST,
88166255Sdelphij	FT_UNKNOWN
89166255Sdelphij};
90166255Sdelphij
91166255Sdelphij#ifndef NO_BZIP2_SUPPORT
92166255Sdelphij#include <bzlib.h>
93166255Sdelphij
94166255Sdelphij#define BZ2_SUFFIX	".bz2"
95309850Sdelphij#define BZIP2_MAGIC	"BZh"
96166255Sdelphij#endif
97166255Sdelphij
98166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
99166255Sdelphij#define Z_SUFFIX	".Z"
100166255Sdelphij#define Z_MAGIC		"\037\235"
101166255Sdelphij#endif
102166255Sdelphij
103194579Sdelphij#ifndef NO_PACK_SUPPORT
104194579Sdelphij#define PACK_MAGIC	"\037\036"
105194579Sdelphij#endif
106194579Sdelphij
107226184Sdelphij#ifndef NO_XZ_SUPPORT
108226184Sdelphij#include <lzma.h>
109226184Sdelphij#define XZ_SUFFIX	".xz"
110226184Sdelphij#define XZ_MAGIC	"\3757zXZ"
111226184Sdelphij#endif
112226184Sdelphij
113166255Sdelphij#define GZ_SUFFIX	".gz"
114166255Sdelphij
115166255Sdelphij#define BUFLEN		(64 * 1024)
116166255Sdelphij
117166255Sdelphij#define GZIP_MAGIC0	0x1F
118166255Sdelphij#define GZIP_MAGIC1	0x8B
119166255Sdelphij#define GZIP_OMAGIC1	0x9E
120166255Sdelphij
121166255Sdelphij#define GZIP_TIMESTAMP	(off_t)4
122166255Sdelphij#define GZIP_ORIGNAME	(off_t)10
123166255Sdelphij
124166255Sdelphij#define HEAD_CRC	0x02
125166255Sdelphij#define EXTRA_FIELD	0x04
126166255Sdelphij#define ORIG_NAME	0x08
127166255Sdelphij#define COMMENT		0x10
128166255Sdelphij
129166255Sdelphij#define OS_CODE		3	/* Unix */
130166255Sdelphij
131166255Sdelphijtypedef struct {
132166255Sdelphij    const char	*zipped;
133166255Sdelphij    int		ziplen;
134166255Sdelphij    const char	*normal;	/* for unzip - must not be longer than zipped */
135166255Sdelphij} suffixes_t;
136166255Sdelphijstatic suffixes_t suffixes[] = {
137166255Sdelphij#define	SUFFIX(Z, N) {Z, sizeof Z - 1, N}
138166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S .xxx */
139166255Sdelphij#ifndef SMALL
140166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),
141166255Sdelphij	SUFFIX(".z",		""),
142166255Sdelphij	SUFFIX("-gz",		""),
143166255Sdelphij	SUFFIX("-z",		""),
144166255Sdelphij	SUFFIX("_z",		""),
145166255Sdelphij	SUFFIX(".taz",		".tar"),
146166255Sdelphij	SUFFIX(".tgz",		".tar"),
147166255Sdelphij#ifndef NO_BZIP2_SUPPORT
148166255Sdelphij	SUFFIX(BZ2_SUFFIX,	""),
149176980Srwatson	SUFFIX(".tbz",		".tar"),
150176980Srwatson	SUFFIX(".tbz2",		".tar"),
151166255Sdelphij#endif
152166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
153166255Sdelphij	SUFFIX(Z_SUFFIX,	""),
154166255Sdelphij#endif
155226184Sdelphij#ifndef NO_XZ_SUPPORT
156226184Sdelphij	SUFFIX(XZ_SUFFIX,	""),
157226184Sdelphij#endif
158166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S "" */
159166255Sdelphij#endif /* SMALL */
160166255Sdelphij#undef SUFFIX
161166255Sdelphij};
162307697Saraujo#define NUM_SUFFIXES (nitems(suffixes))
163195988Sdelphij#define SUFFIX_MAXLEN	30
164195988Sdelphij
165326559Sdelphijstatic	const char	gzip_version[] = "FreeBSD gzip 20171121";
166166255Sdelphij
167166255Sdelphij#ifndef SMALL
168166255Sdelphijstatic	const char	gzip_copyright[] = \
169166255Sdelphij"   Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
170166255Sdelphij"   All rights reserved.\n"
171166255Sdelphij"\n"
172166255Sdelphij"   Redistribution and use in source and binary forms, with or without\n"
173166255Sdelphij"   modification, are permitted provided that the following conditions\n"
174166255Sdelphij"   are met:\n"
175166255Sdelphij"   1. Redistributions of source code must retain the above copyright\n"
176166255Sdelphij"      notice, this list of conditions and the following disclaimer.\n"
177166255Sdelphij"   2. Redistributions in binary form must reproduce the above copyright\n"
178166255Sdelphij"      notice, this list of conditions and the following disclaimer in the\n"
179166255Sdelphij"      documentation and/or other materials provided with the distribution.\n"
180166255Sdelphij"\n"
181166255Sdelphij"   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
182166255Sdelphij"   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
183166255Sdelphij"   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
184166255Sdelphij"   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
185166255Sdelphij"   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
186166255Sdelphij"   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
187166255Sdelphij"   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
188166255Sdelphij"   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
189166255Sdelphij"   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
190166255Sdelphij"   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
191166255Sdelphij"   SUCH DAMAGE.";
192166255Sdelphij#endif
193166255Sdelphij
194166255Sdelphijstatic	int	cflag;			/* stdout mode */
195166255Sdelphijstatic	int	dflag;			/* decompress mode */
196166255Sdelphijstatic	int	lflag;			/* list mode */
197166255Sdelphijstatic	int	numflag = 6;		/* gzip -1..-9 value */
198166255Sdelphij
199326559Sdelphijstatic	const char *remove_file = NULL;	/* file to be removed upon SIGINT */
200326559Sdelphij
201326559Sdelphijstatic	int	fflag;			/* force mode */
202166255Sdelphij#ifndef SMALL
203170053Sdelphijstatic	int	kflag;			/* don't delete input files */
204166255Sdelphijstatic	int	nflag;			/* don't save name/timestamp */
205166255Sdelphijstatic	int	Nflag;			/* don't restore name/timestamp */
206166255Sdelphijstatic	int	qflag;			/* quiet mode */
207166255Sdelphijstatic	int	rflag;			/* recursive mode */
208166255Sdelphijstatic	int	tflag;			/* test */
209166255Sdelphijstatic	int	vflag;			/* verbose mode */
210326559Sdelphijstatic	sig_atomic_t print_info = 0;
211166255Sdelphij#else
212166255Sdelphij#define		qflag	0
213166255Sdelphij#define		tflag	0
214166255Sdelphij#endif
215166255Sdelphij
216166255Sdelphijstatic	int	exit_value = 0;		/* exit value */
217166255Sdelphij
218326559Sdelphijstatic	const char *infile;		/* name of file coming in */
219166255Sdelphij
220226184Sdelphijstatic	void	maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
221226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
222226184Sdelphij    !defined(NO_XZ_SUPPORT)
223226184Sdelphijstatic	void	maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
224166255Sdelphij#endif
225226184Sdelphijstatic	void	maybe_warn(const char *fmt, ...) __printflike(1, 2);
226226184Sdelphijstatic	void	maybe_warnx(const char *fmt, ...) __printflike(1, 2);
227166255Sdelphijstatic	enum filetype file_gettype(u_char *);
228166255Sdelphij#ifdef SMALL
229166255Sdelphij#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
230166255Sdelphij#endif
231166255Sdelphijstatic	off_t	gz_compress(int, int, off_t *, const char *, uint32_t);
232166255Sdelphijstatic	off_t	gz_uncompress(int, int, char *, size_t, off_t *, const char *);
233166255Sdelphijstatic	off_t	file_compress(char *, char *, size_t);
234166255Sdelphijstatic	off_t	file_uncompress(char *, char *, size_t);
235166255Sdelphijstatic	void	handle_pathname(char *);
236166255Sdelphijstatic	void	handle_file(char *, struct stat *);
237166255Sdelphijstatic	void	handle_stdin(void);
238166255Sdelphijstatic	void	handle_stdout(void);
239166255Sdelphijstatic	void	print_ratio(off_t, off_t, FILE *);
240166255Sdelphijstatic	void	print_list(int fd, off_t, const char *, time_t);
241226184Sdelphijstatic	void	usage(void) __dead2;
242226184Sdelphijstatic	void	display_version(void) __dead2;
243166255Sdelphij#ifndef SMALL
244166255Sdelphijstatic	void	display_license(void);
245166255Sdelphij#endif
246166255Sdelphijstatic	const suffixes_t *check_suffix(char *, int);
247166255Sdelphijstatic	ssize_t	read_retry(int, void *, size_t);
248326559Sdelphijstatic	ssize_t	write_retry(int, const void *, size_t);
249166255Sdelphij
250166255Sdelphij#ifdef SMALL
251326559Sdelphij#define infile_set(f,t) infile_set(f)
252326559Sdelphij#endif
253326559Sdelphijstatic	void	infile_set(const char *newinfile, off_t total);
254326559Sdelphij
255326559Sdelphij#ifdef SMALL
256166255Sdelphij#define unlink_input(f, sb) unlink(f)
257326559Sdelphij#define check_siginfo() /* nothing */
258326559Sdelphij#define setup_signals() /* nothing */
259326559Sdelphij#define infile_newdata(t) /* nothing */
260166255Sdelphij#else
261326559Sdelphijstatic	off_t	infile_total;		/* total expected to read/write */
262326559Sdelphijstatic	off_t	infile_current;		/* current read/write */
263326559Sdelphij
264326559Sdelphijstatic	void	check_siginfo(void);
265166255Sdelphijstatic	off_t	cat_fd(unsigned char *, size_t, off_t *, int fd);
266166255Sdelphijstatic	void	prepend_gzip(char *, int *, char ***);
267166255Sdelphijstatic	void	handle_dir(char *);
268166255Sdelphijstatic	void	print_verbage(const char *, const char *, off_t, off_t);
269166255Sdelphijstatic	void	print_test(const char *, int);
270166255Sdelphijstatic	void	copymodes(int fd, const struct stat *, const char *file);
271166255Sdelphijstatic	int	check_outfile(const char *outfile);
272326559Sdelphijstatic	void	setup_signals(void);
273326559Sdelphijstatic	void	infile_newdata(size_t newdata);
274326559Sdelphijstatic	void	infile_clear(void);
275166255Sdelphij#endif
276166255Sdelphij
277166255Sdelphij#ifndef NO_BZIP2_SUPPORT
278166255Sdelphijstatic	off_t	unbzip2(int, int, char *, size_t, off_t *);
279166255Sdelphij#endif
280166255Sdelphij
281166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
282166255Sdelphijstatic	FILE 	*zdopen(int);
283166255Sdelphijstatic	off_t	zuncompress(FILE *, FILE *, char *, size_t, off_t *);
284166255Sdelphij#endif
285166255Sdelphij
286194579Sdelphij#ifndef NO_PACK_SUPPORT
287194579Sdelphijstatic	off_t	unpack(int, int, char *, size_t, off_t *);
288194579Sdelphij#endif
289194579Sdelphij
290226184Sdelphij#ifndef NO_XZ_SUPPORT
291226184Sdelphijstatic	off_t	unxz(int, int, char *, size_t, off_t *);
292226184Sdelphij#endif
293166255Sdelphij
294166255Sdelphij#ifdef SMALL
295166255Sdelphij#define getopt_long(a,b,c,d,e) getopt(a,b,c)
296166255Sdelphij#else
297166255Sdelphijstatic const struct option longopts[] = {
298166255Sdelphij	{ "stdout",		no_argument,		0,	'c' },
299166255Sdelphij	{ "to-stdout",		no_argument,		0,	'c' },
300166255Sdelphij	{ "decompress",		no_argument,		0,	'd' },
301166255Sdelphij	{ "uncompress",		no_argument,		0,	'd' },
302166255Sdelphij	{ "force",		no_argument,		0,	'f' },
303166255Sdelphij	{ "help",		no_argument,		0,	'h' },
304170053Sdelphij	{ "keep",		no_argument,		0,	'k' },
305166255Sdelphij	{ "list",		no_argument,		0,	'l' },
306166255Sdelphij	{ "no-name",		no_argument,		0,	'n' },
307166255Sdelphij	{ "name",		no_argument,		0,	'N' },
308166255Sdelphij	{ "quiet",		no_argument,		0,	'q' },
309166255Sdelphij	{ "recursive",		no_argument,		0,	'r' },
310166255Sdelphij	{ "suffix",		required_argument,	0,	'S' },
311166255Sdelphij	{ "test",		no_argument,		0,	't' },
312166255Sdelphij	{ "verbose",		no_argument,		0,	'v' },
313166255Sdelphij	{ "version",		no_argument,		0,	'V' },
314166255Sdelphij	{ "fast",		no_argument,		0,	'1' },
315166255Sdelphij	{ "best",		no_argument,		0,	'9' },
316166255Sdelphij	{ "ascii",		no_argument,		0,	'a' },
317166255Sdelphij	{ "license",		no_argument,		0,	'L' },
318166255Sdelphij	{ NULL,			no_argument,		0,	0 },
319166255Sdelphij};
320166255Sdelphij#endif
321166255Sdelphij
322166255Sdelphijint
323166255Sdelphijmain(int argc, char **argv)
324166255Sdelphij{
325166255Sdelphij	const char *progname = getprogname();
326166255Sdelphij#ifndef SMALL
327166255Sdelphij	char *gzip;
328166255Sdelphij	int len;
329166255Sdelphij#endif
330166255Sdelphij	int ch;
331166255Sdelphij
332326559Sdelphij	setup_signals();
333326559Sdelphij
334166255Sdelphij#ifndef SMALL
335166255Sdelphij	if ((gzip = getenv("GZIP")) != NULL)
336166255Sdelphij		prepend_gzip(gzip, &argc, &argv);
337166255Sdelphij#endif
338166255Sdelphij
339166255Sdelphij	/*
340166255Sdelphij	 * XXX
341166255Sdelphij	 * handle being called `gunzip', `zcat' and `gzcat'
342166255Sdelphij	 */
343166255Sdelphij	if (strcmp(progname, "gunzip") == 0)
344166255Sdelphij		dflag = 1;
345166255Sdelphij	else if (strcmp(progname, "zcat") == 0 ||
346166255Sdelphij		 strcmp(progname, "gzcat") == 0)
347166255Sdelphij		dflag = cflag = 1;
348166255Sdelphij
349166255Sdelphij#ifdef SMALL
350222210Sdelphij#define OPT_LIST "123456789cdhlV"
351166255Sdelphij#else
352170053Sdelphij#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
353166255Sdelphij#endif
354166255Sdelphij
355166255Sdelphij	while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
356166255Sdelphij		switch (ch) {
357166255Sdelphij		case '1': case '2': case '3':
358166255Sdelphij		case '4': case '5': case '6':
359166255Sdelphij		case '7': case '8': case '9':
360166255Sdelphij			numflag = ch - '0';
361166255Sdelphij			break;
362166255Sdelphij		case 'c':
363166255Sdelphij			cflag = 1;
364166255Sdelphij			break;
365166255Sdelphij		case 'd':
366166255Sdelphij			dflag = 1;
367166255Sdelphij			break;
368166255Sdelphij		case 'l':
369166255Sdelphij			lflag = 1;
370166255Sdelphij			dflag = 1;
371166255Sdelphij			break;
372166255Sdelphij		case 'V':
373166255Sdelphij			display_version();
374166255Sdelphij			/* NOTREACHED */
375166255Sdelphij#ifndef SMALL
376166255Sdelphij		case 'a':
377166255Sdelphij			fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
378166255Sdelphij			break;
379166255Sdelphij		case 'f':
380166255Sdelphij			fflag = 1;
381166255Sdelphij			break;
382170053Sdelphij		case 'k':
383170053Sdelphij			kflag = 1;
384170053Sdelphij			break;
385166255Sdelphij		case 'L':
386166255Sdelphij			display_license();
387166255Sdelphij			/* NOT REACHED */
388166255Sdelphij		case 'N':
389166255Sdelphij			nflag = 0;
390166255Sdelphij			Nflag = 1;
391166255Sdelphij			break;
392166255Sdelphij		case 'n':
393166255Sdelphij			nflag = 1;
394166255Sdelphij			Nflag = 0;
395166255Sdelphij			break;
396166255Sdelphij		case 'q':
397166255Sdelphij			qflag = 1;
398166255Sdelphij			break;
399166255Sdelphij		case 'r':
400166255Sdelphij			rflag = 1;
401166255Sdelphij			break;
402166255Sdelphij		case 'S':
403166255Sdelphij			len = strlen(optarg);
404166255Sdelphij			if (len != 0) {
405195988Sdelphij				if (len > SUFFIX_MAXLEN)
406195988Sdelphij					errx(1, "incorrect suffix: '%s': too long", optarg);
407166255Sdelphij				suffixes[0].zipped = optarg;
408166255Sdelphij				suffixes[0].ziplen = len;
409166255Sdelphij			} else {
410166255Sdelphij				suffixes[NUM_SUFFIXES - 1].zipped = "";
411166255Sdelphij				suffixes[NUM_SUFFIXES - 1].ziplen = 0;
412166255Sdelphij			}
413166255Sdelphij			break;
414166255Sdelphij		case 't':
415166255Sdelphij			cflag = 1;
416166255Sdelphij			tflag = 1;
417166255Sdelphij			dflag = 1;
418166255Sdelphij			break;
419166255Sdelphij		case 'v':
420166255Sdelphij			vflag = 1;
421166255Sdelphij			break;
422166255Sdelphij#endif
423166255Sdelphij		default:
424166255Sdelphij			usage();
425166255Sdelphij			/* NOTREACHED */
426166255Sdelphij		}
427166255Sdelphij	}
428166255Sdelphij	argv += optind;
429166255Sdelphij	argc -= optind;
430166255Sdelphij
431166255Sdelphij	if (argc == 0) {
432166255Sdelphij		if (dflag)	/* stdin mode */
433166255Sdelphij			handle_stdin();
434166255Sdelphij		else		/* stdout mode */
435166255Sdelphij			handle_stdout();
436166255Sdelphij	} else {
437166255Sdelphij		do {
438166255Sdelphij			handle_pathname(argv[0]);
439166255Sdelphij		} while (*++argv);
440166255Sdelphij	}
441166255Sdelphij#ifndef SMALL
442166255Sdelphij	if (qflag == 0 && lflag && argc > 1)
443166255Sdelphij		print_list(-1, 0, "(totals)", 0);
444166255Sdelphij#endif
445166255Sdelphij	exit(exit_value);
446166255Sdelphij}
447166255Sdelphij
448166255Sdelphij/* maybe print a warning */
449166255Sdelphijvoid
450166255Sdelphijmaybe_warn(const char *fmt, ...)
451166255Sdelphij{
452166255Sdelphij	va_list ap;
453166255Sdelphij
454166255Sdelphij	if (qflag == 0) {
455166255Sdelphij		va_start(ap, fmt);
456166255Sdelphij		vwarn(fmt, ap);
457166255Sdelphij		va_end(ap);
458166255Sdelphij	}
459166255Sdelphij	if (exit_value == 0)
460166255Sdelphij		exit_value = 1;
461166255Sdelphij}
462166255Sdelphij
463166255Sdelphij/* ... without an errno. */
464166255Sdelphijvoid
465166255Sdelphijmaybe_warnx(const char *fmt, ...)
466166255Sdelphij{
467166255Sdelphij	va_list ap;
468166255Sdelphij
469166255Sdelphij	if (qflag == 0) {
470166255Sdelphij		va_start(ap, fmt);
471166255Sdelphij		vwarnx(fmt, ap);
472166255Sdelphij		va_end(ap);
473166255Sdelphij	}
474166255Sdelphij	if (exit_value == 0)
475166255Sdelphij		exit_value = 1;
476166255Sdelphij}
477166255Sdelphij
478166255Sdelphij/* maybe print an error */
479166255Sdelphijvoid
480166255Sdelphijmaybe_err(const char *fmt, ...)
481166255Sdelphij{
482166255Sdelphij	va_list ap;
483166255Sdelphij
484166255Sdelphij	if (qflag == 0) {
485166255Sdelphij		va_start(ap, fmt);
486166255Sdelphij		vwarn(fmt, ap);
487166255Sdelphij		va_end(ap);
488166255Sdelphij	}
489166255Sdelphij	exit(2);
490166255Sdelphij}
491166255Sdelphij
492226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
493226184Sdelphij    !defined(NO_XZ_SUPPORT)
494166255Sdelphij/* ... without an errno. */
495166255Sdelphijvoid
496166255Sdelphijmaybe_errx(const char *fmt, ...)
497166255Sdelphij{
498166255Sdelphij	va_list ap;
499166255Sdelphij
500166255Sdelphij	if (qflag == 0) {
501166255Sdelphij		va_start(ap, fmt);
502166255Sdelphij		vwarnx(fmt, ap);
503166255Sdelphij		va_end(ap);
504166255Sdelphij	}
505166255Sdelphij	exit(2);
506166255Sdelphij}
507166255Sdelphij#endif
508166255Sdelphij
509166255Sdelphij#ifndef SMALL
510166255Sdelphij/* split up $GZIP and prepend it to the argument list */
511166255Sdelphijstatic void
512166255Sdelphijprepend_gzip(char *gzip, int *argc, char ***argv)
513166255Sdelphij{
514166255Sdelphij	char *s, **nargv, **ac;
515166255Sdelphij	int nenvarg = 0, i;
516166255Sdelphij
517166255Sdelphij	/* scan how many arguments there are */
518166255Sdelphij	for (s = gzip;;) {
519166255Sdelphij		while (*s == ' ' || *s == '\t')
520166255Sdelphij			s++;
521166255Sdelphij		if (*s == 0)
522166255Sdelphij			goto count_done;
523166255Sdelphij		nenvarg++;
524166255Sdelphij		while (*s != ' ' && *s != '\t')
525166255Sdelphij			if (*s++ == 0)
526166255Sdelphij				goto count_done;
527166255Sdelphij	}
528166255Sdelphijcount_done:
529166255Sdelphij	/* punt early */
530166255Sdelphij	if (nenvarg == 0)
531166255Sdelphij		return;
532166255Sdelphij
533166255Sdelphij	*argc += nenvarg;
534166255Sdelphij	ac = *argv;
535166255Sdelphij
536166255Sdelphij	nargv = (char **)malloc((*argc + 1) * sizeof(char *));
537166255Sdelphij	if (nargv == NULL)
538166255Sdelphij		maybe_err("malloc");
539166255Sdelphij
540166255Sdelphij	/* stash this away */
541166255Sdelphij	*argv = nargv;
542166255Sdelphij
543166255Sdelphij	/* copy the program name first */
544166255Sdelphij	i = 0;
545166255Sdelphij	nargv[i++] = *(ac++);
546166255Sdelphij
547166255Sdelphij	/* take a copy of $GZIP and add it to the array */
548166255Sdelphij	s = strdup(gzip);
549166255Sdelphij	if (s == NULL)
550166255Sdelphij		maybe_err("strdup");
551166255Sdelphij	for (;;) {
552166255Sdelphij		/* Skip whitespaces. */
553166255Sdelphij		while (*s == ' ' || *s == '\t')
554166255Sdelphij			s++;
555166255Sdelphij		if (*s == 0)
556166255Sdelphij			goto copy_done;
557166255Sdelphij		nargv[i++] = s;
558166255Sdelphij		/* Find the end of this argument. */
559166255Sdelphij		while (*s != ' ' && *s != '\t')
560166255Sdelphij			if (*s++ == 0)
561166255Sdelphij				/* Argument followed by NUL. */
562166255Sdelphij				goto copy_done;
563166255Sdelphij		/* Terminate by overwriting ' ' or '\t' with NUL. */
564166255Sdelphij		*s++ = 0;
565166255Sdelphij	}
566166255Sdelphijcopy_done:
567166255Sdelphij
568166255Sdelphij	/* copy the original arguments and a NULL */
569166255Sdelphij	while (*ac)
570166255Sdelphij		nargv[i++] = *(ac++);
571166255Sdelphij	nargv[i] = NULL;
572166255Sdelphij}
573166255Sdelphij#endif
574166255Sdelphij
575166255Sdelphij/* compress input to output. Return bytes read, -1 on error */
576166255Sdelphijstatic off_t
577166255Sdelphijgz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
578166255Sdelphij{
579166255Sdelphij	z_stream z;
580166255Sdelphij	char *outbufp, *inbufp;
581166255Sdelphij	off_t in_tot = 0, out_tot = 0;
582166255Sdelphij	ssize_t in_size;
583166255Sdelphij	int i, error;
584166255Sdelphij	uLong crc;
585166255Sdelphij#ifdef SMALL
586166255Sdelphij	static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
587166255Sdelphij				 0, 0, 0, 0,
588166255Sdelphij				 0, OS_CODE };
589166255Sdelphij#endif
590166255Sdelphij
591166255Sdelphij	outbufp = malloc(BUFLEN);
592166255Sdelphij	inbufp = malloc(BUFLEN);
593166255Sdelphij	if (outbufp == NULL || inbufp == NULL) {
594166255Sdelphij		maybe_err("malloc failed");
595166255Sdelphij		goto out;
596166255Sdelphij	}
597166255Sdelphij
598166255Sdelphij	memset(&z, 0, sizeof z);
599166255Sdelphij	z.zalloc = Z_NULL;
600166255Sdelphij	z.zfree = Z_NULL;
601166255Sdelphij	z.opaque = 0;
602166255Sdelphij
603166255Sdelphij#ifdef SMALL
604166255Sdelphij	memcpy(outbufp, header, sizeof header);
605166255Sdelphij	i = sizeof header;
606166255Sdelphij#else
607166255Sdelphij	if (nflag != 0) {
608166255Sdelphij		mtime = 0;
609166255Sdelphij		origname = "";
610166255Sdelphij	}
611166255Sdelphij
612326559Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
613166255Sdelphij		     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
614166255Sdelphij		     *origname ? ORIG_NAME : 0,
615166255Sdelphij		     mtime & 0xff,
616166255Sdelphij		     (mtime >> 8) & 0xff,
617166255Sdelphij		     (mtime >> 16) & 0xff,
618166255Sdelphij		     (mtime >> 24) & 0xff,
619166255Sdelphij		     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
620166255Sdelphij		     OS_CODE, origname);
621326559Sdelphij	if (i >= BUFLEN)
622166255Sdelphij		/* this need PATH_MAX > BUFLEN ... */
623166255Sdelphij		maybe_err("snprintf");
624166255Sdelphij	if (*origname)
625166255Sdelphij		i++;
626166255Sdelphij#endif
627166255Sdelphij
628166255Sdelphij	z.next_out = (unsigned char *)outbufp + i;
629166255Sdelphij	z.avail_out = BUFLEN - i;
630166255Sdelphij
631166255Sdelphij	error = deflateInit2(&z, numflag, Z_DEFLATED,
632166255Sdelphij			     (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
633166255Sdelphij	if (error != Z_OK) {
634166255Sdelphij		maybe_warnx("deflateInit2 failed");
635166255Sdelphij		in_tot = -1;
636166255Sdelphij		goto out;
637166255Sdelphij	}
638166255Sdelphij
639166255Sdelphij	crc = crc32(0L, Z_NULL, 0);
640166255Sdelphij	for (;;) {
641166255Sdelphij		if (z.avail_out == 0) {
642326559Sdelphij			if (write_retry(out, outbufp, BUFLEN) != BUFLEN) {
643166255Sdelphij				maybe_warn("write");
644166255Sdelphij				out_tot = -1;
645166255Sdelphij				goto out;
646166255Sdelphij			}
647166255Sdelphij
648166255Sdelphij			out_tot += BUFLEN;
649166255Sdelphij			z.next_out = (unsigned char *)outbufp;
650166255Sdelphij			z.avail_out = BUFLEN;
651166255Sdelphij		}
652166255Sdelphij
653166255Sdelphij		if (z.avail_in == 0) {
654166255Sdelphij			in_size = read(in, inbufp, BUFLEN);
655166255Sdelphij			if (in_size < 0) {
656166255Sdelphij				maybe_warn("read");
657166255Sdelphij				in_tot = -1;
658166255Sdelphij				goto out;
659166255Sdelphij			}
660166255Sdelphij			if (in_size == 0)
661166255Sdelphij				break;
662326559Sdelphij			infile_newdata(in_size);
663166255Sdelphij
664166255Sdelphij			crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
665166255Sdelphij			in_tot += in_size;
666166255Sdelphij			z.next_in = (unsigned char *)inbufp;
667166255Sdelphij			z.avail_in = in_size;
668166255Sdelphij		}
669166255Sdelphij
670166255Sdelphij		error = deflate(&z, Z_NO_FLUSH);
671166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
672166255Sdelphij			maybe_warnx("deflate failed");
673166255Sdelphij			in_tot = -1;
674166255Sdelphij			goto out;
675166255Sdelphij		}
676166255Sdelphij	}
677166255Sdelphij
678166255Sdelphij	/* clean up */
679166255Sdelphij	for (;;) {
680166255Sdelphij		size_t len;
681166255Sdelphij		ssize_t w;
682166255Sdelphij
683166255Sdelphij		error = deflate(&z, Z_FINISH);
684166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
685166255Sdelphij			maybe_warnx("deflate failed");
686166255Sdelphij			in_tot = -1;
687166255Sdelphij			goto out;
688166255Sdelphij		}
689166255Sdelphij
690166255Sdelphij		len = (char *)z.next_out - outbufp;
691166255Sdelphij
692326559Sdelphij		w = write_retry(out, outbufp, len);
693166255Sdelphij		if (w == -1 || (size_t)w != len) {
694166255Sdelphij			maybe_warn("write");
695166255Sdelphij			out_tot = -1;
696166255Sdelphij			goto out;
697166255Sdelphij		}
698166255Sdelphij		out_tot += len;
699166255Sdelphij		z.next_out = (unsigned char *)outbufp;
700166255Sdelphij		z.avail_out = BUFLEN;
701166255Sdelphij
702166255Sdelphij		if (error == Z_STREAM_END)
703166255Sdelphij			break;
704166255Sdelphij	}
705166255Sdelphij
706166255Sdelphij	if (deflateEnd(&z) != Z_OK) {
707166255Sdelphij		maybe_warnx("deflateEnd failed");
708166255Sdelphij		in_tot = -1;
709166255Sdelphij		goto out;
710166255Sdelphij	}
711166255Sdelphij
712326559Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
713166255Sdelphij		 (int)crc & 0xff,
714166255Sdelphij		 (int)(crc >> 8) & 0xff,
715166255Sdelphij		 (int)(crc >> 16) & 0xff,
716166255Sdelphij		 (int)(crc >> 24) & 0xff,
717166255Sdelphij		 (int)in_tot & 0xff,
718166255Sdelphij		 (int)(in_tot >> 8) & 0xff,
719166255Sdelphij		 (int)(in_tot >> 16) & 0xff,
720166255Sdelphij		 (int)(in_tot >> 24) & 0xff);
721166255Sdelphij	if (i != 8)
722166255Sdelphij		maybe_err("snprintf");
723326559Sdelphij	if (write_retry(out, outbufp, i) != i) {
724166255Sdelphij		maybe_warn("write");
725166255Sdelphij		in_tot = -1;
726166255Sdelphij	} else
727166255Sdelphij		out_tot += i;
728166255Sdelphij
729166255Sdelphijout:
730166255Sdelphij	if (inbufp != NULL)
731166255Sdelphij		free(inbufp);
732166255Sdelphij	if (outbufp != NULL)
733166255Sdelphij		free(outbufp);
734166255Sdelphij	if (gsizep)
735166255Sdelphij		*gsizep = out_tot;
736166255Sdelphij	return in_tot;
737166255Sdelphij}
738166255Sdelphij
739166255Sdelphij/*
740166255Sdelphij * uncompress input to output then close the input.  return the
741166255Sdelphij * uncompressed size written, and put the compressed sized read
742166255Sdelphij * into `*gsizep'.
743166255Sdelphij */
744166255Sdelphijstatic off_t
745166255Sdelphijgz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
746166255Sdelphij	      const char *filename)
747166255Sdelphij{
748166255Sdelphij	z_stream z;
749166255Sdelphij	char *outbufp, *inbufp;
750166255Sdelphij	off_t out_tot = -1, in_tot = 0;
751166255Sdelphij	uint32_t out_sub_tot = 0;
752166255Sdelphij	enum {
753166255Sdelphij		GZSTATE_MAGIC0,
754166255Sdelphij		GZSTATE_MAGIC1,
755166255Sdelphij		GZSTATE_METHOD,
756166255Sdelphij		GZSTATE_FLAGS,
757166255Sdelphij		GZSTATE_SKIPPING,
758166255Sdelphij		GZSTATE_EXTRA,
759166255Sdelphij		GZSTATE_EXTRA2,
760166255Sdelphij		GZSTATE_EXTRA3,
761166255Sdelphij		GZSTATE_ORIGNAME,
762166255Sdelphij		GZSTATE_COMMENT,
763166255Sdelphij		GZSTATE_HEAD_CRC1,
764166255Sdelphij		GZSTATE_HEAD_CRC2,
765166255Sdelphij		GZSTATE_INIT,
766166255Sdelphij		GZSTATE_READ,
767166255Sdelphij		GZSTATE_CRC,
768166255Sdelphij		GZSTATE_LEN,
769166255Sdelphij	} state = GZSTATE_MAGIC0;
770166255Sdelphij	int flags = 0, skip_count = 0;
771166255Sdelphij	int error = Z_STREAM_ERROR, done_reading = 0;
772166255Sdelphij	uLong crc = 0;
773166255Sdelphij	ssize_t wr;
774166255Sdelphij	int needmore = 0;
775166255Sdelphij
776166255Sdelphij#define ADVANCE()       { z.next_in++; z.avail_in--; }
777166255Sdelphij
778166255Sdelphij	if ((outbufp = malloc(BUFLEN)) == NULL) {
779166255Sdelphij		maybe_err("malloc failed");
780166255Sdelphij		goto out2;
781166255Sdelphij	}
782166255Sdelphij	if ((inbufp = malloc(BUFLEN)) == NULL) {
783166255Sdelphij		maybe_err("malloc failed");
784166255Sdelphij		goto out1;
785166255Sdelphij	}
786166255Sdelphij
787166255Sdelphij	memset(&z, 0, sizeof z);
788166255Sdelphij	z.avail_in = prelen;
789166255Sdelphij	z.next_in = (unsigned char *)pre;
790166255Sdelphij	z.avail_out = BUFLEN;
791166255Sdelphij	z.next_out = (unsigned char *)outbufp;
792166255Sdelphij	z.zalloc = NULL;
793166255Sdelphij	z.zfree = NULL;
794166255Sdelphij	z.opaque = 0;
795166255Sdelphij
796166255Sdelphij	in_tot = prelen;
797166255Sdelphij	out_tot = 0;
798166255Sdelphij
799166255Sdelphij	for (;;) {
800326559Sdelphij		check_siginfo();
801166255Sdelphij		if ((z.avail_in == 0 || needmore) && done_reading == 0) {
802166255Sdelphij			ssize_t in_size;
803166255Sdelphij
804166255Sdelphij			if (z.avail_in > 0) {
805166255Sdelphij				memmove(inbufp, z.next_in, z.avail_in);
806166255Sdelphij			}
807166255Sdelphij			z.next_in = (unsigned char *)inbufp;
808166255Sdelphij			in_size = read(in, z.next_in + z.avail_in,
809166255Sdelphij			    BUFLEN - z.avail_in);
810166255Sdelphij
811166255Sdelphij			if (in_size == -1) {
812166255Sdelphij				maybe_warn("failed to read stdin");
813166255Sdelphij				goto stop_and_fail;
814166255Sdelphij			} else if (in_size == 0) {
815166255Sdelphij				done_reading = 1;
816166255Sdelphij			}
817326559Sdelphij			infile_newdata(in_size);
818166255Sdelphij
819166255Sdelphij			z.avail_in += in_size;
820166255Sdelphij			needmore = 0;
821166255Sdelphij
822166255Sdelphij			in_tot += in_size;
823166255Sdelphij		}
824166255Sdelphij		if (z.avail_in == 0) {
825166255Sdelphij			if (done_reading && state != GZSTATE_MAGIC0) {
826166255Sdelphij				maybe_warnx("%s: unexpected end of file",
827166255Sdelphij					    filename);
828166255Sdelphij				goto stop_and_fail;
829166255Sdelphij			}
830166255Sdelphij			goto stop;
831166255Sdelphij		}
832166255Sdelphij		switch (state) {
833166255Sdelphij		case GZSTATE_MAGIC0:
834166255Sdelphij			if (*z.next_in != GZIP_MAGIC0) {
835166255Sdelphij				if (in_tot > 0) {
836166255Sdelphij					maybe_warnx("%s: trailing garbage "
837166255Sdelphij						    "ignored", filename);
838290024Sdelphij					exit_value = 2;
839166255Sdelphij					goto stop;
840166255Sdelphij				}
841166255Sdelphij				maybe_warnx("input not gziped (MAGIC0)");
842166255Sdelphij				goto stop_and_fail;
843166255Sdelphij			}
844166255Sdelphij			ADVANCE();
845166255Sdelphij			state++;
846166255Sdelphij			out_sub_tot = 0;
847166255Sdelphij			crc = crc32(0L, Z_NULL, 0);
848166255Sdelphij			break;
849166255Sdelphij
850166255Sdelphij		case GZSTATE_MAGIC1:
851166255Sdelphij			if (*z.next_in != GZIP_MAGIC1 &&
852166255Sdelphij			    *z.next_in != GZIP_OMAGIC1) {
853166255Sdelphij				maybe_warnx("input not gziped (MAGIC1)");
854166255Sdelphij				goto stop_and_fail;
855166255Sdelphij			}
856166255Sdelphij			ADVANCE();
857166255Sdelphij			state++;
858166255Sdelphij			break;
859166255Sdelphij
860166255Sdelphij		case GZSTATE_METHOD:
861166255Sdelphij			if (*z.next_in != Z_DEFLATED) {
862166255Sdelphij				maybe_warnx("unknown compression method");
863166255Sdelphij				goto stop_and_fail;
864166255Sdelphij			}
865166255Sdelphij			ADVANCE();
866166255Sdelphij			state++;
867166255Sdelphij			break;
868166255Sdelphij
869166255Sdelphij		case GZSTATE_FLAGS:
870166255Sdelphij			flags = *z.next_in;
871166255Sdelphij			ADVANCE();
872166255Sdelphij			skip_count = 6;
873166255Sdelphij			state++;
874166255Sdelphij			break;
875166255Sdelphij
876166255Sdelphij		case GZSTATE_SKIPPING:
877166255Sdelphij			if (skip_count > 0) {
878166255Sdelphij				skip_count--;
879166255Sdelphij				ADVANCE();
880166255Sdelphij			} else
881166255Sdelphij				state++;
882166255Sdelphij			break;
883166255Sdelphij
884166255Sdelphij		case GZSTATE_EXTRA:
885166255Sdelphij			if ((flags & EXTRA_FIELD) == 0) {
886166255Sdelphij				state = GZSTATE_ORIGNAME;
887166255Sdelphij				break;
888166255Sdelphij			}
889166255Sdelphij			skip_count = *z.next_in;
890166255Sdelphij			ADVANCE();
891166255Sdelphij			state++;
892166255Sdelphij			break;
893166255Sdelphij
894166255Sdelphij		case GZSTATE_EXTRA2:
895166255Sdelphij			skip_count |= ((*z.next_in) << 8);
896166255Sdelphij			ADVANCE();
897166255Sdelphij			state++;
898166255Sdelphij			break;
899166255Sdelphij
900166255Sdelphij		case GZSTATE_EXTRA3:
901166255Sdelphij			if (skip_count > 0) {
902166255Sdelphij				skip_count--;
903166255Sdelphij				ADVANCE();
904166255Sdelphij			} else
905166255Sdelphij				state++;
906166255Sdelphij			break;
907166255Sdelphij
908166255Sdelphij		case GZSTATE_ORIGNAME:
909166255Sdelphij			if ((flags & ORIG_NAME) == 0) {
910166255Sdelphij				state++;
911166255Sdelphij				break;
912166255Sdelphij			}
913166255Sdelphij			if (*z.next_in == 0)
914166255Sdelphij				state++;
915166255Sdelphij			ADVANCE();
916166255Sdelphij			break;
917166255Sdelphij
918166255Sdelphij		case GZSTATE_COMMENT:
919166255Sdelphij			if ((flags & COMMENT) == 0) {
920166255Sdelphij				state++;
921166255Sdelphij				break;
922166255Sdelphij			}
923166255Sdelphij			if (*z.next_in == 0)
924166255Sdelphij				state++;
925166255Sdelphij			ADVANCE();
926166255Sdelphij			break;
927166255Sdelphij
928166255Sdelphij		case GZSTATE_HEAD_CRC1:
929166255Sdelphij			if (flags & HEAD_CRC)
930166255Sdelphij				skip_count = 2;
931166255Sdelphij			else
932166255Sdelphij				skip_count = 0;
933166255Sdelphij			state++;
934166255Sdelphij			break;
935166255Sdelphij
936166255Sdelphij		case GZSTATE_HEAD_CRC2:
937166255Sdelphij			if (skip_count > 0) {
938166255Sdelphij				skip_count--;
939166255Sdelphij				ADVANCE();
940166255Sdelphij			} else
941166255Sdelphij				state++;
942166255Sdelphij			break;
943166255Sdelphij
944166255Sdelphij		case GZSTATE_INIT:
945166255Sdelphij			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
946166255Sdelphij				maybe_warnx("failed to inflateInit");
947166255Sdelphij				goto stop_and_fail;
948166255Sdelphij			}
949166255Sdelphij			state++;
950166255Sdelphij			break;
951166255Sdelphij
952166255Sdelphij		case GZSTATE_READ:
953166255Sdelphij			error = inflate(&z, Z_FINISH);
954166255Sdelphij			switch (error) {
955166255Sdelphij			/* Z_BUF_ERROR goes with Z_FINISH... */
956166255Sdelphij			case Z_BUF_ERROR:
957213044Sdelphij				if (z.avail_out > 0 && !done_reading)
958213044Sdelphij					continue;
959222210Sdelphij
960166255Sdelphij			case Z_STREAM_END:
961166255Sdelphij			case Z_OK:
962166255Sdelphij				break;
963166255Sdelphij
964166255Sdelphij			case Z_NEED_DICT:
965166255Sdelphij				maybe_warnx("Z_NEED_DICT error");
966166255Sdelphij				goto stop_and_fail;
967166255Sdelphij			case Z_DATA_ERROR:
968166255Sdelphij				maybe_warnx("data stream error");
969166255Sdelphij				goto stop_and_fail;
970166255Sdelphij			case Z_STREAM_ERROR:
971166255Sdelphij				maybe_warnx("internal stream error");
972166255Sdelphij				goto stop_and_fail;
973166255Sdelphij			case Z_MEM_ERROR:
974166255Sdelphij				maybe_warnx("memory allocation error");
975166255Sdelphij				goto stop_and_fail;
976166255Sdelphij
977166255Sdelphij			default:
978166255Sdelphij				maybe_warn("unknown error from inflate(): %d",
979166255Sdelphij				    error);
980166255Sdelphij			}
981166255Sdelphij			wr = BUFLEN - z.avail_out;
982166255Sdelphij
983166255Sdelphij			if (wr != 0) {
984166255Sdelphij				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
985166255Sdelphij				if (
986166255Sdelphij#ifndef SMALL
987166255Sdelphij				    /* don't write anything with -t */
988166255Sdelphij				    tflag == 0 &&
989166255Sdelphij#endif
990326559Sdelphij				    write_retry(out, outbufp, wr) != wr) {
991166255Sdelphij					maybe_warn("error writing to output");
992166255Sdelphij					goto stop_and_fail;
993166255Sdelphij				}
994166255Sdelphij
995166255Sdelphij				out_tot += wr;
996166255Sdelphij				out_sub_tot += wr;
997166255Sdelphij			}
998166255Sdelphij
999166255Sdelphij			if (error == Z_STREAM_END) {
1000166255Sdelphij				inflateEnd(&z);
1001166255Sdelphij				state++;
1002166255Sdelphij			}
1003166255Sdelphij
1004166255Sdelphij			z.next_out = (unsigned char *)outbufp;
1005166255Sdelphij			z.avail_out = BUFLEN;
1006166255Sdelphij
1007166255Sdelphij			break;
1008166255Sdelphij		case GZSTATE_CRC:
1009166255Sdelphij			{
1010166255Sdelphij				uLong origcrc;
1011166255Sdelphij
1012166255Sdelphij				if (z.avail_in < 4) {
1013166255Sdelphij					if (!done_reading) {
1014166255Sdelphij						needmore = 1;
1015166255Sdelphij						continue;
1016166255Sdelphij					}
1017166255Sdelphij					maybe_warnx("truncated input");
1018166255Sdelphij					goto stop_and_fail;
1019166255Sdelphij				}
1020336661Sdelphij				origcrc = le32dec(&z.next_in[0]);
1021166255Sdelphij				if (origcrc != crc) {
1022166255Sdelphij					maybe_warnx("invalid compressed"
1023166255Sdelphij					     " data--crc error");
1024166255Sdelphij					goto stop_and_fail;
1025166255Sdelphij				}
1026166255Sdelphij			}
1027166255Sdelphij
1028166255Sdelphij			z.avail_in -= 4;
1029166255Sdelphij			z.next_in += 4;
1030166255Sdelphij
1031166255Sdelphij			if (!z.avail_in && done_reading) {
1032166255Sdelphij				goto stop;
1033166255Sdelphij			}
1034166255Sdelphij			state++;
1035166255Sdelphij			break;
1036166255Sdelphij		case GZSTATE_LEN:
1037166255Sdelphij			{
1038166255Sdelphij				uLong origlen;
1039166255Sdelphij
1040166255Sdelphij				if (z.avail_in < 4) {
1041166255Sdelphij					if (!done_reading) {
1042166255Sdelphij						needmore = 1;
1043166255Sdelphij						continue;
1044166255Sdelphij					}
1045166255Sdelphij					maybe_warnx("truncated input");
1046166255Sdelphij					goto stop_and_fail;
1047166255Sdelphij				}
1048336661Sdelphij				origlen = le32dec(&z.next_in[0]);
1049166255Sdelphij
1050166255Sdelphij				if (origlen != out_sub_tot) {
1051166255Sdelphij					maybe_warnx("invalid compressed"
1052166255Sdelphij					     " data--length error");
1053166255Sdelphij					goto stop_and_fail;
1054166255Sdelphij				}
1055166255Sdelphij			}
1056166255Sdelphij
1057166255Sdelphij			z.avail_in -= 4;
1058166255Sdelphij			z.next_in += 4;
1059166255Sdelphij
1060166255Sdelphij			if (error < 0) {
1061166255Sdelphij				maybe_warnx("decompression error");
1062166255Sdelphij				goto stop_and_fail;
1063166255Sdelphij			}
1064166255Sdelphij			state = GZSTATE_MAGIC0;
1065166255Sdelphij			break;
1066166255Sdelphij		}
1067166255Sdelphij		continue;
1068166255Sdelphijstop_and_fail:
1069166255Sdelphij		out_tot = -1;
1070166255Sdelphijstop:
1071166255Sdelphij		break;
1072166255Sdelphij	}
1073166255Sdelphij	if (state > GZSTATE_INIT)
1074166255Sdelphij		inflateEnd(&z);
1075166255Sdelphij
1076166255Sdelphij	free(inbufp);
1077166255Sdelphijout1:
1078166255Sdelphij	free(outbufp);
1079166255Sdelphijout2:
1080166255Sdelphij	if (gsizep)
1081166255Sdelphij		*gsizep = in_tot;
1082166255Sdelphij	return (out_tot);
1083166255Sdelphij}
1084166255Sdelphij
1085166255Sdelphij#ifndef SMALL
1086166255Sdelphij/*
1087166255Sdelphij * set the owner, mode, flags & utimes using the given file descriptor.
1088166255Sdelphij * file is only used in possible warning messages.
1089166255Sdelphij */
1090166255Sdelphijstatic void
1091166255Sdelphijcopymodes(int fd, const struct stat *sbp, const char *file)
1092166255Sdelphij{
1093278896Sjilles	struct timespec times[2];
1094166255Sdelphij	struct stat sb;
1095166255Sdelphij
1096166255Sdelphij	/*
1097166255Sdelphij	 * If we have no info on the input, give this file some
1098166255Sdelphij	 * default values and return..
1099166255Sdelphij	 */
1100166255Sdelphij	if (sbp == NULL) {
1101166255Sdelphij		mode_t mask = umask(022);
1102166255Sdelphij
1103166255Sdelphij		(void)fchmod(fd, DEFFILEMODE & ~mask);
1104166255Sdelphij		(void)umask(mask);
1105326559Sdelphij		return;
1106166255Sdelphij	}
1107166255Sdelphij	sb = *sbp;
1108166255Sdelphij
1109166255Sdelphij	/* if the chown fails, remove set-id bits as-per compress(1) */
1110166255Sdelphij	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1111166255Sdelphij		if (errno != EPERM)
1112166255Sdelphij			maybe_warn("couldn't fchown: %s", file);
1113166255Sdelphij		sb.st_mode &= ~(S_ISUID|S_ISGID);
1114166255Sdelphij	}
1115166255Sdelphij
1116166255Sdelphij	/* we only allow set-id and the 9 normal permission bits */
1117166255Sdelphij	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1118166255Sdelphij	if (fchmod(fd, sb.st_mode) < 0)
1119166255Sdelphij		maybe_warn("couldn't fchmod: %s", file);
1120166255Sdelphij
1121278896Sjilles	times[0] = sb.st_atim;
1122278896Sjilles	times[1] = sb.st_mtim;
1123278896Sjilles	if (futimens(fd, times) < 0)
1124278896Sjilles		maybe_warn("couldn't futimens: %s", file);
1125176970Srwatson
1126176970Srwatson	/* only try flags if they exist already */
1127176970Srwatson        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1128176970Srwatson		maybe_warn("couldn't fchflags: %s", file);
1129166255Sdelphij}
1130166255Sdelphij#endif
1131166255Sdelphij
1132166255Sdelphij/* what sort of file is this? */
1133166255Sdelphijstatic enum filetype
1134166255Sdelphijfile_gettype(u_char *buf)
1135166255Sdelphij{
1136166255Sdelphij
1137166255Sdelphij	if (buf[0] == GZIP_MAGIC0 &&
1138166255Sdelphij	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1139166255Sdelphij		return FT_GZIP;
1140166255Sdelphij	else
1141166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1142166255Sdelphij	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1143166255Sdelphij	    buf[3] >= '0' && buf[3] <= '9')
1144166255Sdelphij		return FT_BZIP2;
1145166255Sdelphij	else
1146166255Sdelphij#endif
1147166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1148166255Sdelphij	if (memcmp(buf, Z_MAGIC, 2) == 0)
1149166255Sdelphij		return FT_Z;
1150166255Sdelphij	else
1151166255Sdelphij#endif
1152194579Sdelphij#ifndef NO_PACK_SUPPORT
1153194579Sdelphij	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1154194579Sdelphij		return FT_PACK;
1155194579Sdelphij	else
1156194579Sdelphij#endif
1157226184Sdelphij#ifndef NO_XZ_SUPPORT
1158226184Sdelphij	if (memcmp(buf, XZ_MAGIC, 4) == 0)	/* XXX: We only have 4 bytes */
1159226184Sdelphij		return FT_XZ;
1160226184Sdelphij	else
1161226184Sdelphij#endif
1162166255Sdelphij		return FT_UNKNOWN;
1163166255Sdelphij}
1164166255Sdelphij
1165166255Sdelphij#ifndef SMALL
1166166255Sdelphij/* check the outfile is OK. */
1167166255Sdelphijstatic int
1168166255Sdelphijcheck_outfile(const char *outfile)
1169166255Sdelphij{
1170166255Sdelphij	struct stat sb;
1171166255Sdelphij	int ok = 1;
1172166255Sdelphij
1173166255Sdelphij	if (lflag == 0 && stat(outfile, &sb) == 0) {
1174166255Sdelphij		if (fflag)
1175166255Sdelphij			unlink(outfile);
1176166255Sdelphij		else if (isatty(STDIN_FILENO)) {
1177166255Sdelphij			char ans[10] = { 'n', '\0' };	/* default */
1178166255Sdelphij
1179166255Sdelphij			fprintf(stderr, "%s already exists -- do you wish to "
1180166255Sdelphij					"overwrite (y or n)? " , outfile);
1181166255Sdelphij			(void)fgets(ans, sizeof(ans) - 1, stdin);
1182166255Sdelphij			if (ans[0] != 'y' && ans[0] != 'Y') {
1183166363Sdelphij				fprintf(stderr, "\tnot overwriting\n");
1184166255Sdelphij				ok = 0;
1185166255Sdelphij			} else
1186166255Sdelphij				unlink(outfile);
1187166255Sdelphij		} else {
1188166255Sdelphij			maybe_warnx("%s already exists -- skipping", outfile);
1189166255Sdelphij			ok = 0;
1190166255Sdelphij		}
1191166255Sdelphij	}
1192166255Sdelphij	return ok;
1193166255Sdelphij}
1194166255Sdelphij
1195166255Sdelphijstatic void
1196166255Sdelphijunlink_input(const char *file, const struct stat *sb)
1197166255Sdelphij{
1198166255Sdelphij	struct stat nsb;
1199166255Sdelphij
1200170053Sdelphij	if (kflag)
1201170053Sdelphij		return;
1202166255Sdelphij	if (stat(file, &nsb) != 0)
1203213927Sbcr		/* Must be gone already */
1204166255Sdelphij		return;
1205166255Sdelphij	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1206166255Sdelphij		/* Definitely a different file */
1207166255Sdelphij		return;
1208166255Sdelphij	unlink(file);
1209166255Sdelphij}
1210207247Sdelphij
1211207247Sdelphijstatic void
1212326559Sdelphijgot_sigint(int signo __unused)
1213207247Sdelphij{
1214207247Sdelphij
1215207247Sdelphij	if (remove_file != NULL)
1216207247Sdelphij		unlink(remove_file);
1217207284Sdelphij	_exit(2);
1218207247Sdelphij}
1219326559Sdelphij
1220326559Sdelphijstatic void
1221326559Sdelphijgot_siginfo(int signo __unused)
1222326559Sdelphij{
1223326559Sdelphij
1224326559Sdelphij	print_info = 1;
1225326559Sdelphij}
1226326559Sdelphij
1227326559Sdelphijstatic void
1228326559Sdelphijsetup_signals(void)
1229326559Sdelphij{
1230326559Sdelphij
1231326559Sdelphij	signal(SIGINFO, got_siginfo);
1232326559Sdelphij	signal(SIGINT, got_sigint);
1233326559Sdelphij}
1234326559Sdelphij
1235326559Sdelphijstatic	void
1236326559Sdelphijinfile_newdata(size_t newdata)
1237326559Sdelphij{
1238326559Sdelphij
1239326559Sdelphij	infile_current += newdata;
1240326559Sdelphij}
1241166255Sdelphij#endif
1242166255Sdelphij
1243326559Sdelphijstatic	void
1244326559Sdelphijinfile_set(const char *newinfile, off_t total)
1245326559Sdelphij{
1246326559Sdelphij
1247326559Sdelphij	if (newinfile)
1248326559Sdelphij		infile = newinfile;
1249326559Sdelphij#ifndef SMALL
1250326559Sdelphij	infile_total = total;
1251326559Sdelphij#endif
1252326559Sdelphij}
1253326559Sdelphij
1254326559Sdelphijstatic	void
1255326559Sdelphijinfile_clear(void)
1256326559Sdelphij{
1257326559Sdelphij
1258326559Sdelphij	infile = NULL;
1259326559Sdelphij#ifndef SMALL
1260326559Sdelphij	infile_total = infile_current = 0;
1261326559Sdelphij#endif
1262326559Sdelphij}
1263326559Sdelphij
1264166255Sdelphijstatic const suffixes_t *
1265166255Sdelphijcheck_suffix(char *file, int xlate)
1266166255Sdelphij{
1267166255Sdelphij	const suffixes_t *s;
1268166255Sdelphij	int len = strlen(file);
1269166255Sdelphij	char *sp;
1270166255Sdelphij
1271166255Sdelphij	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1272166255Sdelphij		/* if it doesn't fit in "a.suf", don't bother */
1273166255Sdelphij		if (s->ziplen >= len)
1274166255Sdelphij			continue;
1275166255Sdelphij		sp = file + len - s->ziplen;
1276166255Sdelphij		if (strcmp(s->zipped, sp) != 0)
1277166255Sdelphij			continue;
1278166255Sdelphij		if (xlate)
1279166255Sdelphij			strcpy(sp, s->normal);
1280166255Sdelphij		return s;
1281166255Sdelphij	}
1282166255Sdelphij	return NULL;
1283166255Sdelphij}
1284166255Sdelphij
1285166255Sdelphij/*
1286166255Sdelphij * compress the given file: create a corresponding .gz file and remove the
1287166255Sdelphij * original.
1288166255Sdelphij */
1289166255Sdelphijstatic off_t
1290166255Sdelphijfile_compress(char *file, char *outfile, size_t outsize)
1291166255Sdelphij{
1292166255Sdelphij	int in;
1293166255Sdelphij	int out;
1294326559Sdelphij	off_t size, in_size;
1295166255Sdelphij#ifndef SMALL
1296166255Sdelphij	struct stat isb, osb;
1297166255Sdelphij	const suffixes_t *suff;
1298166255Sdelphij#endif
1299166255Sdelphij
1300166255Sdelphij	in = open(file, O_RDONLY);
1301166255Sdelphij	if (in == -1) {
1302166255Sdelphij		maybe_warn("can't open %s", file);
1303209017Sdelphij		return (-1);
1304166255Sdelphij	}
1305166255Sdelphij
1306208888Sdelphij#ifndef SMALL
1307208888Sdelphij	if (fstat(in, &isb) != 0) {
1308208888Sdelphij		maybe_warn("couldn't stat: %s", file);
1309208888Sdelphij		close(in);
1310209017Sdelphij		return (-1);
1311208888Sdelphij	}
1312208888Sdelphij#endif
1313208888Sdelphij
1314326559Sdelphij#ifndef SMALL
1315326559Sdelphij	if (fstat(in, &isb) != 0) {
1316326559Sdelphij		close(in);
1317326559Sdelphij		maybe_warn("can't stat %s", file);
1318326559Sdelphij		return -1;
1319326559Sdelphij	}
1320326559Sdelphij	infile_set(file, isb.st_size);
1321326559Sdelphij#endif
1322326559Sdelphij
1323166255Sdelphij	if (cflag == 0) {
1324166255Sdelphij#ifndef SMALL
1325209017Sdelphij		if (isb.st_nlink > 1 && fflag == 0) {
1326326559Sdelphij			maybe_warnx("%s has %ju other link%s -- "
1327326559Sdelphij				    "skipping", file,
1328326559Sdelphij				    (uintmax_t)isb.st_nlink - 1,
1329326559Sdelphij				    isb.st_nlink == 1 ? "" : "s");
1330208889Sdelphij			close(in);
1331326559Sdelphij			return -1;
1332208889Sdelphij		}
1333166255Sdelphij
1334209017Sdelphij		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1335209017Sdelphij		    suff->zipped[0] != 0) {
1336166255Sdelphij			maybe_warnx("%s already has %s suffix -- unchanged",
1337209017Sdelphij			    file, suff->zipped);
1338166255Sdelphij			close(in);
1339209017Sdelphij			return (-1);
1340166255Sdelphij		}
1341166255Sdelphij#endif
1342166255Sdelphij
1343166255Sdelphij		/* Add (usually) .gz to filename */
1344166255Sdelphij		if ((size_t)snprintf(outfile, outsize, "%s%s",
1345209017Sdelphij		    file, suffixes[0].zipped) >= outsize)
1346195988Sdelphij			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1347209017Sdelphij			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1348166255Sdelphij
1349166255Sdelphij#ifndef SMALL
1350166255Sdelphij		if (check_outfile(outfile) == 0) {
1351166255Sdelphij			close(in);
1352209017Sdelphij			return (-1);
1353166255Sdelphij		}
1354166255Sdelphij#endif
1355166255Sdelphij	}
1356166255Sdelphij
1357166255Sdelphij	if (cflag == 0) {
1358166255Sdelphij		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1359166255Sdelphij		if (out == -1) {
1360166255Sdelphij			maybe_warn("could not create output: %s", outfile);
1361166255Sdelphij			fclose(stdin);
1362209017Sdelphij			return (-1);
1363166255Sdelphij		}
1364207247Sdelphij#ifndef SMALL
1365207247Sdelphij		remove_file = outfile;
1366207247Sdelphij#endif
1367166255Sdelphij	} else
1368166255Sdelphij		out = STDOUT_FILENO;
1369166255Sdelphij
1370326559Sdelphij	in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1371166255Sdelphij
1372166255Sdelphij	(void)close(in);
1373166255Sdelphij
1374166255Sdelphij	/*
1375326559Sdelphij	 * If there was an error, in_size will be -1.
1376166255Sdelphij	 * If we compressed to stdout, just return the size.
1377166255Sdelphij	 * Otherwise stat the file and check it is the correct size.
1378166255Sdelphij	 * We only blow away the file if we can stat the output and it
1379166255Sdelphij	 * has the expected size.
1380166255Sdelphij	 */
1381166255Sdelphij	if (cflag != 0)
1382326559Sdelphij		return in_size == -1 ? -1 : size;
1383166255Sdelphij
1384166255Sdelphij#ifndef SMALL
1385166255Sdelphij	if (fstat(out, &osb) != 0) {
1386166255Sdelphij		maybe_warn("couldn't stat: %s", outfile);
1387166255Sdelphij		goto bad_outfile;
1388166255Sdelphij	}
1389166255Sdelphij
1390166255Sdelphij	if (osb.st_size != size) {
1391209017Sdelphij		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1392209017Sdelphij		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1393166255Sdelphij		goto bad_outfile;
1394166255Sdelphij	}
1395166255Sdelphij
1396166255Sdelphij	copymodes(out, &isb, outfile);
1397207247Sdelphij	remove_file = NULL;
1398166255Sdelphij#endif
1399166255Sdelphij	if (close(out) == -1)
1400166255Sdelphij		maybe_warn("couldn't close output");
1401166255Sdelphij
1402166255Sdelphij	/* output is good, ok to delete input */
1403166255Sdelphij	unlink_input(file, &isb);
1404209017Sdelphij	return (size);
1405166255Sdelphij
1406166255Sdelphij#ifndef SMALL
1407166255Sdelphij    bad_outfile:
1408166255Sdelphij	if (close(out) == -1)
1409166255Sdelphij		maybe_warn("couldn't close output");
1410166255Sdelphij
1411166255Sdelphij	maybe_warnx("leaving original %s", file);
1412166255Sdelphij	unlink(outfile);
1413209017Sdelphij	return (size);
1414166255Sdelphij#endif
1415166255Sdelphij}
1416166255Sdelphij
1417166255Sdelphij/* uncompress the given file and remove the original */
1418166255Sdelphijstatic off_t
1419166255Sdelphijfile_uncompress(char *file, char *outfile, size_t outsize)
1420166255Sdelphij{
1421166255Sdelphij	struct stat isb, osb;
1422166255Sdelphij	off_t size;
1423166255Sdelphij	ssize_t rbytes;
1424166255Sdelphij	unsigned char header1[4];
1425166255Sdelphij	enum filetype method;
1426194508Sdelphij	int fd, ofd, zfd = -1;
1427337521Sdelphij	int error;
1428326559Sdelphij	size_t in_size;
1429166255Sdelphij#ifndef SMALL
1430206387Sdelphij	ssize_t rv;
1431166255Sdelphij	time_t timestamp = 0;
1432281500Sdelphij	char name[PATH_MAX + 1];
1433166255Sdelphij#endif
1434166255Sdelphij
1435166255Sdelphij	/* gather the old name info */
1436166255Sdelphij
1437166255Sdelphij	fd = open(file, O_RDONLY);
1438166255Sdelphij	if (fd < 0) {
1439166255Sdelphij		maybe_warn("can't open %s", file);
1440166255Sdelphij		goto lose;
1441166255Sdelphij	}
1442326559Sdelphij	if (fstat(fd, &isb) != 0) {
1443326559Sdelphij		close(fd);
1444326559Sdelphij		maybe_warn("can't stat %s", file);
1445326559Sdelphij		goto lose;
1446326559Sdelphij	}
1447326559Sdelphij	if (S_ISREG(isb.st_mode))
1448326559Sdelphij		in_size = isb.st_size;
1449326559Sdelphij	else
1450326559Sdelphij		in_size = 0;
1451326559Sdelphij	infile_set(file, in_size);
1452166255Sdelphij
1453166255Sdelphij	strlcpy(outfile, file, outsize);
1454166255Sdelphij	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1455166255Sdelphij		maybe_warnx("%s: unknown suffix -- ignored", file);
1456166255Sdelphij		goto lose;
1457166255Sdelphij	}
1458166255Sdelphij
1459166255Sdelphij	rbytes = read(fd, header1, sizeof header1);
1460166255Sdelphij	if (rbytes != sizeof header1) {
1461166255Sdelphij		/* we don't want to fail here. */
1462166255Sdelphij#ifndef SMALL
1463166255Sdelphij		if (fflag)
1464166255Sdelphij			goto lose;
1465166255Sdelphij#endif
1466166255Sdelphij		if (rbytes == -1)
1467166255Sdelphij			maybe_warn("can't read %s", file);
1468166255Sdelphij		else
1469166255Sdelphij			goto unexpected_EOF;
1470166255Sdelphij		goto lose;
1471166255Sdelphij	}
1472326559Sdelphij	infile_newdata(rbytes);
1473166255Sdelphij
1474166255Sdelphij	method = file_gettype(header1);
1475166255Sdelphij#ifndef SMALL
1476166255Sdelphij	if (fflag == 0 && method == FT_UNKNOWN) {
1477166255Sdelphij		maybe_warnx("%s: not in gzip format", file);
1478166255Sdelphij		goto lose;
1479166255Sdelphij	}
1480166255Sdelphij
1481166255Sdelphij#endif
1482166255Sdelphij
1483166255Sdelphij#ifndef SMALL
1484166255Sdelphij	if (method == FT_GZIP && Nflag) {
1485166255Sdelphij		unsigned char ts[4];	/* timestamp */
1486166255Sdelphij
1487166255Sdelphij		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1488194916Sdelphij		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1489166255Sdelphij			goto unexpected_EOF;
1490166255Sdelphij		if (rv == -1) {
1491166255Sdelphij			if (!fflag)
1492166255Sdelphij				maybe_warn("can't read %s", file);
1493166255Sdelphij			goto lose;
1494166255Sdelphij		}
1495326559Sdelphij		infile_newdata(rv);
1496336661Sdelphij		timestamp = le32dec(&ts[0]);
1497166255Sdelphij
1498166255Sdelphij		if (header1[3] & ORIG_NAME) {
1499281540Sdelphij			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
1500166255Sdelphij			if (rbytes < 0) {
1501166255Sdelphij				maybe_warn("can't read %s", file);
1502166255Sdelphij				goto lose;
1503166255Sdelphij			}
1504281540Sdelphij			if (name[0] != '\0') {
1505281500Sdelphij				char *dp, *nf;
1506281500Sdelphij
1507281540Sdelphij				/* Make sure that name is NUL-terminated */
1508281540Sdelphij				name[rbytes] = '\0';
1509281540Sdelphij
1510281500Sdelphij				/* strip saved directory name */
1511281500Sdelphij				nf = strrchr(name, '/');
1512281500Sdelphij				if (nf == NULL)
1513281500Sdelphij					nf = name;
1514281500Sdelphij				else
1515281500Sdelphij					nf++;
1516281500Sdelphij
1517166255Sdelphij				/* preserve original directory name */
1518281500Sdelphij				dp = strrchr(file, '/');
1519166255Sdelphij				if (dp == NULL)
1520166255Sdelphij					dp = file;
1521166255Sdelphij				else
1522166255Sdelphij					dp++;
1523166255Sdelphij				snprintf(outfile, outsize, "%.*s%.*s",
1524326559Sdelphij						(int) (dp - file),
1525281500Sdelphij						file, (int) rbytes, nf);
1526166255Sdelphij			}
1527166255Sdelphij		}
1528166255Sdelphij	}
1529166255Sdelphij#endif
1530166255Sdelphij	lseek(fd, 0, SEEK_SET);
1531166255Sdelphij
1532166255Sdelphij	if (cflag == 0 || lflag) {
1533166255Sdelphij#ifndef SMALL
1534166255Sdelphij		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1535312072Skib			maybe_warnx("%s has %ju other links -- skipping",
1536312072Skib			    file, (uintmax_t)isb.st_nlink - 1);
1537166255Sdelphij			goto lose;
1538166255Sdelphij		}
1539166255Sdelphij		if (nflag == 0 && timestamp)
1540166255Sdelphij			isb.st_mtime = timestamp;
1541166255Sdelphij		if (check_outfile(outfile) == 0)
1542166255Sdelphij			goto lose;
1543166255Sdelphij#endif
1544166255Sdelphij	}
1545166255Sdelphij
1546326559Sdelphij	if (cflag)
1547326559Sdelphij		zfd = STDOUT_FILENO;
1548326559Sdelphij	else if (lflag)
1549326559Sdelphij		zfd = -1;
1550326559Sdelphij	else {
1551166255Sdelphij		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1552166255Sdelphij		if (zfd == STDOUT_FILENO) {
1553166255Sdelphij			/* We won't close STDOUT_FILENO later... */
1554166255Sdelphij			zfd = dup(zfd);
1555166255Sdelphij			close(STDOUT_FILENO);
1556166255Sdelphij		}
1557166255Sdelphij		if (zfd == -1) {
1558166255Sdelphij			maybe_warn("can't open %s", outfile);
1559166255Sdelphij			goto lose;
1560166255Sdelphij		}
1561207247Sdelphij		remove_file = outfile;
1562326559Sdelphij	}
1563166255Sdelphij
1564226184Sdelphij	switch (method) {
1565166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1566226184Sdelphij	case FT_BZIP2:
1567166255Sdelphij		/* XXX */
1568166255Sdelphij		if (lflag) {
1569166255Sdelphij			maybe_warnx("no -l with bzip2 files");
1570166255Sdelphij			goto lose;
1571166255Sdelphij		}
1572166255Sdelphij
1573166255Sdelphij		size = unbzip2(fd, zfd, NULL, 0, NULL);
1574226184Sdelphij		break;
1575166255Sdelphij#endif
1576166255Sdelphij
1577166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1578226184Sdelphij	case FT_Z: {
1579166255Sdelphij		FILE *in, *out;
1580166255Sdelphij
1581166255Sdelphij		/* XXX */
1582166255Sdelphij		if (lflag) {
1583166255Sdelphij			maybe_warnx("no -l with Lempel-Ziv files");
1584166255Sdelphij			goto lose;
1585166255Sdelphij		}
1586166255Sdelphij
1587166255Sdelphij		if ((in = zdopen(fd)) == NULL) {
1588166255Sdelphij			maybe_warn("zdopen for read: %s", file);
1589166255Sdelphij			goto lose;
1590166255Sdelphij		}
1591166255Sdelphij
1592166255Sdelphij		out = fdopen(dup(zfd), "w");
1593166255Sdelphij		if (out == NULL) {
1594166255Sdelphij			maybe_warn("fdopen for write: %s", outfile);
1595166255Sdelphij			fclose(in);
1596166255Sdelphij			goto lose;
1597166255Sdelphij		}
1598166255Sdelphij
1599166255Sdelphij		size = zuncompress(in, out, NULL, 0, NULL);
1600166255Sdelphij		/* need to fclose() if ferror() is true... */
1601337521Sdelphij		error = ferror(in);
1602337521Sdelphij		if (error | fclose(in)) {
1603337521Sdelphij			if (error)
1604337521Sdelphij				maybe_warn("failed infile");
1605337521Sdelphij			else
1606337521Sdelphij				maybe_warn("failed infile fclose");
1607337521Sdelphij			if (cflag == 0)
1608337521Sdelphij				unlink(outfile);
1609166255Sdelphij			(void)fclose(out);
1610337521Sdelphij			goto lose;
1611166255Sdelphij		}
1612166255Sdelphij		if (fclose(out) != 0) {
1613166255Sdelphij			maybe_warn("failed outfile fclose");
1614337521Sdelphij			if (cflag == 0)
1615337521Sdelphij				unlink(outfile);
1616166255Sdelphij			goto lose;
1617166255Sdelphij		}
1618226184Sdelphij		break;
1619226184Sdelphij	}
1620166255Sdelphij#endif
1621166255Sdelphij
1622194579Sdelphij#ifndef NO_PACK_SUPPORT
1623226184Sdelphij	case FT_PACK:
1624194579Sdelphij		if (lflag) {
1625194579Sdelphij			maybe_warnx("no -l with packed files");
1626194579Sdelphij			goto lose;
1627194579Sdelphij		}
1628194579Sdelphij
1629194579Sdelphij		size = unpack(fd, zfd, NULL, 0, NULL);
1630226184Sdelphij		break;
1631194579Sdelphij#endif
1632194579Sdelphij
1633226184Sdelphij#ifndef NO_XZ_SUPPORT
1634226184Sdelphij	case FT_XZ:
1635226184Sdelphij		if (lflag) {
1636226184Sdelphij			maybe_warnx("no -l with xz files");
1637226184Sdelphij			goto lose;
1638226184Sdelphij		}
1639226184Sdelphij
1640226184Sdelphij		size = unxz(fd, zfd, NULL, 0, NULL);
1641226184Sdelphij		break;
1642226184Sdelphij#endif
1643226184Sdelphij
1644166255Sdelphij#ifndef SMALL
1645226184Sdelphij	case FT_UNKNOWN:
1646166255Sdelphij		if (lflag) {
1647166255Sdelphij			maybe_warnx("no -l for unknown filetypes");
1648166255Sdelphij			goto lose;
1649166255Sdelphij		}
1650166255Sdelphij		size = cat_fd(NULL, 0, NULL, fd);
1651226184Sdelphij		break;
1652166255Sdelphij#endif
1653226184Sdelphij	default:
1654166255Sdelphij		if (lflag) {
1655326559Sdelphij			print_list(fd, in_size, outfile, isb.st_mtime);
1656166255Sdelphij			close(fd);
1657166255Sdelphij			return -1;	/* XXX */
1658166255Sdelphij		}
1659166255Sdelphij
1660166255Sdelphij		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1661226184Sdelphij		break;
1662166255Sdelphij	}
1663166255Sdelphij
1664166255Sdelphij	if (close(fd) != 0)
1665166255Sdelphij		maybe_warn("couldn't close input");
1666166255Sdelphij	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1667166255Sdelphij		maybe_warn("couldn't close output");
1668166255Sdelphij
1669166255Sdelphij	if (size == -1) {
1670166255Sdelphij		if (cflag == 0)
1671166255Sdelphij			unlink(outfile);
1672166255Sdelphij		maybe_warnx("%s: uncompress failed", file);
1673166255Sdelphij		return -1;
1674166255Sdelphij	}
1675166255Sdelphij
1676166255Sdelphij	/* if testing, or we uncompressed to stdout, this is all we need */
1677166255Sdelphij#ifndef SMALL
1678166255Sdelphij	if (tflag)
1679166255Sdelphij		return size;
1680166255Sdelphij#endif
1681166255Sdelphij	/* if we are uncompressing to stdin, don't remove the file. */
1682166255Sdelphij	if (cflag)
1683166255Sdelphij		return size;
1684166255Sdelphij
1685166255Sdelphij	/*
1686166255Sdelphij	 * if we create a file...
1687166255Sdelphij	 */
1688166255Sdelphij	/*
1689166255Sdelphij	 * if we can't stat the file don't remove the file.
1690166255Sdelphij	 */
1691166255Sdelphij
1692166255Sdelphij	ofd = open(outfile, O_RDWR, 0);
1693166255Sdelphij	if (ofd == -1) {
1694166255Sdelphij		maybe_warn("couldn't open (leaving original): %s",
1695166255Sdelphij			   outfile);
1696166255Sdelphij		return -1;
1697166255Sdelphij	}
1698166255Sdelphij	if (fstat(ofd, &osb) != 0) {
1699166255Sdelphij		maybe_warn("couldn't stat (leaving original): %s",
1700166255Sdelphij			   outfile);
1701166255Sdelphij		close(ofd);
1702166255Sdelphij		return -1;
1703166255Sdelphij	}
1704166255Sdelphij	if (osb.st_size != size) {
1705209017Sdelphij		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1706209017Sdelphij		    (uintmax_t)size, (uintmax_t)osb.st_size);
1707166255Sdelphij		close(ofd);
1708166255Sdelphij		unlink(outfile);
1709166255Sdelphij		return -1;
1710166255Sdelphij	}
1711166255Sdelphij#ifndef SMALL
1712166255Sdelphij	copymodes(ofd, &isb, outfile);
1713207247Sdelphij	remove_file = NULL;
1714166255Sdelphij#endif
1715166255Sdelphij	close(ofd);
1716207247Sdelphij	unlink_input(file, &isb);
1717166255Sdelphij	return size;
1718166255Sdelphij
1719166255Sdelphij    unexpected_EOF:
1720166255Sdelphij	maybe_warnx("%s: unexpected end of file", file);
1721166255Sdelphij    lose:
1722166255Sdelphij	if (fd != -1)
1723166255Sdelphij		close(fd);
1724166255Sdelphij	if (zfd != -1 && zfd != STDOUT_FILENO)
1725327191Sdelphij		close(zfd);
1726166255Sdelphij	return -1;
1727166255Sdelphij}
1728166255Sdelphij
1729166255Sdelphij#ifndef SMALL
1730326559Sdelphijstatic void
1731326559Sdelphijcheck_siginfo(void)
1732326559Sdelphij{
1733326559Sdelphij	if (print_info == 0)
1734326559Sdelphij		return;
1735326559Sdelphij	if (infile) {
1736326559Sdelphij		if (infile_total) {
1737326559Sdelphij			int pcent = (int)((100.0 * infile_current) / infile_total);
1738326559Sdelphij
1739326559Sdelphij			fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n",
1740326559Sdelphij				infile, (unsigned long long)infile_current,
1741326559Sdelphij				(unsigned long long)infile_total, pcent);
1742326559Sdelphij		} else
1743326559Sdelphij			fprintf(stderr, "%s: done %llu bytes\n",
1744326559Sdelphij				infile, (unsigned long long)infile_current);
1745326559Sdelphij	}
1746326559Sdelphij	print_info = 0;
1747326559Sdelphij}
1748326559Sdelphij
1749166255Sdelphijstatic off_t
1750166255Sdelphijcat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1751166255Sdelphij{
1752166255Sdelphij	char buf[BUFLEN];
1753166255Sdelphij	off_t in_tot;
1754166255Sdelphij	ssize_t w;
1755166255Sdelphij
1756166255Sdelphij	in_tot = count;
1757326559Sdelphij	w = write_retry(STDOUT_FILENO, prepend, count);
1758166255Sdelphij	if (w == -1 || (size_t)w != count) {
1759166255Sdelphij		maybe_warn("write to stdout");
1760166255Sdelphij		return -1;
1761166255Sdelphij	}
1762166255Sdelphij	for (;;) {
1763166255Sdelphij		ssize_t rv;
1764166255Sdelphij
1765166255Sdelphij		rv = read(fd, buf, sizeof buf);
1766166255Sdelphij		if (rv == 0)
1767166255Sdelphij			break;
1768166255Sdelphij		if (rv < 0) {
1769166255Sdelphij			maybe_warn("read from fd %d", fd);
1770166255Sdelphij			break;
1771166255Sdelphij		}
1772326559Sdelphij		infile_newdata(rv);
1773166255Sdelphij
1774326559Sdelphij		if (write_retry(STDOUT_FILENO, buf, rv) != rv) {
1775166255Sdelphij			maybe_warn("write to stdout");
1776166255Sdelphij			break;
1777166255Sdelphij		}
1778166255Sdelphij		in_tot += rv;
1779166255Sdelphij	}
1780166255Sdelphij
1781166255Sdelphij	if (gsizep)
1782166255Sdelphij		*gsizep = in_tot;
1783166255Sdelphij	return (in_tot);
1784166255Sdelphij}
1785166255Sdelphij#endif
1786166255Sdelphij
1787166255Sdelphijstatic void
1788166255Sdelphijhandle_stdin(void)
1789166255Sdelphij{
1790326559Sdelphij	struct stat isb;
1791166255Sdelphij	unsigned char header1[4];
1792326559Sdelphij	size_t in_size;
1793166255Sdelphij	off_t usize, gsize;
1794166255Sdelphij	enum filetype method;
1795166255Sdelphij	ssize_t bytes_read;
1796166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1797166255Sdelphij	FILE *in;
1798166255Sdelphij#endif
1799166255Sdelphij
1800166255Sdelphij#ifndef SMALL
1801166255Sdelphij	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1802166255Sdelphij		maybe_warnx("standard input is a terminal -- ignoring");
1803326559Sdelphij		goto out;
1804166255Sdelphij	}
1805166255Sdelphij#endif
1806166255Sdelphij
1807326559Sdelphij	if (fstat(STDIN_FILENO, &isb) < 0) {
1808326559Sdelphij		maybe_warn("fstat");
1809326559Sdelphij		goto out;
1810326559Sdelphij	}
1811326559Sdelphij	if (S_ISREG(isb.st_mode))
1812326559Sdelphij		in_size = isb.st_size;
1813326559Sdelphij	else
1814326559Sdelphij		in_size = 0;
1815326559Sdelphij	infile_set("(stdin)", in_size);
1816326559Sdelphij
1817166255Sdelphij	if (lflag) {
1818326559Sdelphij		print_list(STDIN_FILENO, in_size, infile, isb.st_mtime);
1819326559Sdelphij		goto out;
1820166255Sdelphij	}
1821166255Sdelphij
1822166255Sdelphij	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1823166255Sdelphij	if (bytes_read == -1) {
1824166255Sdelphij		maybe_warn("can't read stdin");
1825326559Sdelphij		goto out;
1826166255Sdelphij	} else if (bytes_read != sizeof(header1)) {
1827166255Sdelphij		maybe_warnx("(stdin): unexpected end of file");
1828326559Sdelphij		goto out;
1829166255Sdelphij	}
1830166255Sdelphij
1831166255Sdelphij	method = file_gettype(header1);
1832166255Sdelphij	switch (method) {
1833166255Sdelphij	default:
1834166255Sdelphij#ifndef SMALL
1835166255Sdelphij		if (fflag == 0) {
1836166255Sdelphij			maybe_warnx("unknown compression format");
1837326559Sdelphij			goto out;
1838166255Sdelphij		}
1839166255Sdelphij		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1840166255Sdelphij		break;
1841166255Sdelphij#endif
1842166255Sdelphij	case FT_GZIP:
1843326559Sdelphij		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1844166255Sdelphij			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1845166255Sdelphij		break;
1846166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1847166255Sdelphij	case FT_BZIP2:
1848166255Sdelphij		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1849166255Sdelphij				(char *)header1, sizeof header1, &gsize);
1850166255Sdelphij		break;
1851166255Sdelphij#endif
1852166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1853166255Sdelphij	case FT_Z:
1854166255Sdelphij		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1855166255Sdelphij			maybe_warnx("zopen of stdin");
1856326559Sdelphij			goto out;
1857166255Sdelphij		}
1858166255Sdelphij
1859226184Sdelphij		usize = zuncompress(in, stdout, (char *)header1,
1860226184Sdelphij		    sizeof header1, &gsize);
1861166255Sdelphij		fclose(in);
1862166255Sdelphij		break;
1863166255Sdelphij#endif
1864194579Sdelphij#ifndef NO_PACK_SUPPORT
1865194579Sdelphij	case FT_PACK:
1866194579Sdelphij		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1867194579Sdelphij			       (char *)header1, sizeof header1, &gsize);
1868194579Sdelphij		break;
1869194579Sdelphij#endif
1870226184Sdelphij#ifndef NO_XZ_SUPPORT
1871226184Sdelphij	case FT_XZ:
1872226184Sdelphij		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1873226184Sdelphij			     (char *)header1, sizeof header1, &gsize);
1874226184Sdelphij		break;
1875226184Sdelphij#endif
1876166255Sdelphij	}
1877166255Sdelphij
1878166255Sdelphij#ifndef SMALL
1879166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1880166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1881166255Sdelphij	if (vflag && tflag)
1882166255Sdelphij		print_test("(stdin)", usize != -1);
1883326559Sdelphij#else
1884326559Sdelphij	(void)&usize;
1885326559Sdelphij#endif
1886166255Sdelphij
1887326559Sdelphijout:
1888326559Sdelphij	infile_clear();
1889166255Sdelphij}
1890166255Sdelphij
1891166255Sdelphijstatic void
1892166255Sdelphijhandle_stdout(void)
1893166255Sdelphij{
1894326559Sdelphij	off_t gsize;
1895326559Sdelphij#ifndef SMALL
1896326559Sdelphij	off_t usize;
1897166255Sdelphij	struct stat sb;
1898166255Sdelphij	time_t systime;
1899166255Sdelphij	uint32_t mtime;
1900166255Sdelphij	int ret;
1901166255Sdelphij
1902326559Sdelphij	infile_set("(stdout)", 0);
1903326559Sdelphij
1904166255Sdelphij	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1905166255Sdelphij		maybe_warnx("standard output is a terminal -- ignoring");
1906166255Sdelphij		return;
1907166255Sdelphij	}
1908326559Sdelphij
1909273507Sdelphij	/* If stdin is a file use its mtime, otherwise use current time */
1910166255Sdelphij	ret = fstat(STDIN_FILENO, &sb);
1911166255Sdelphij	if (ret < 0) {
1912166255Sdelphij		maybe_warn("Can't stat stdin");
1913166255Sdelphij		return;
1914166255Sdelphij	}
1915166255Sdelphij
1916326559Sdelphij	if (S_ISREG(sb.st_mode)) {
1917326559Sdelphij		infile_set("(stdout)", sb.st_size);
1918166255Sdelphij		mtime = (uint32_t)sb.st_mtime;
1919326559Sdelphij	} else {
1920166255Sdelphij		systime = time(NULL);
1921166255Sdelphij		if (systime == -1) {
1922166255Sdelphij			maybe_warn("time");
1923166255Sdelphij			return;
1924326559Sdelphij		}
1925166255Sdelphij		mtime = (uint32_t)systime;
1926166255Sdelphij	}
1927166255Sdelphij
1928326559Sdelphij	usize =
1929326559Sdelphij#endif
1930326559Sdelphij		gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1931166255Sdelphij#ifndef SMALL
1932166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1933166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1934326559Sdelphij#endif
1935166255Sdelphij}
1936166255Sdelphij
1937166255Sdelphij/* do what is asked for, for the path name */
1938166255Sdelphijstatic void
1939166255Sdelphijhandle_pathname(char *path)
1940166255Sdelphij{
1941166255Sdelphij	char *opath = path, *s = NULL;
1942166255Sdelphij	ssize_t len;
1943166255Sdelphij	int slen;
1944166255Sdelphij	struct stat sb;
1945166255Sdelphij
1946166255Sdelphij	/* check for stdout/stdin */
1947166255Sdelphij	if (path[0] == '-' && path[1] == '\0') {
1948166255Sdelphij		if (dflag)
1949166255Sdelphij			handle_stdin();
1950166255Sdelphij		else
1951166255Sdelphij			handle_stdout();
1952166255Sdelphij		return;
1953166255Sdelphij	}
1954166255Sdelphij
1955166255Sdelphijretry:
1956222287Sdelphij	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1957222287Sdelphij	    lstat(path, &sb) != 0)) {
1958166255Sdelphij		/* lets try <path>.gz if we're decompressing */
1959166255Sdelphij		if (dflag && s == NULL && errno == ENOENT) {
1960166255Sdelphij			len = strlen(path);
1961166255Sdelphij			slen = suffixes[0].ziplen;
1962166255Sdelphij			s = malloc(len + slen + 1);
1963166255Sdelphij			if (s == NULL)
1964166255Sdelphij				maybe_err("malloc");
1965166255Sdelphij			memcpy(s, path, len);
1966166255Sdelphij			memcpy(s + len, suffixes[0].zipped, slen + 1);
1967166255Sdelphij			path = s;
1968166255Sdelphij			goto retry;
1969166255Sdelphij		}
1970166255Sdelphij		maybe_warn("can't stat: %s", opath);
1971166255Sdelphij		goto out;
1972166255Sdelphij	}
1973166255Sdelphij
1974166255Sdelphij	if (S_ISDIR(sb.st_mode)) {
1975166255Sdelphij#ifndef SMALL
1976166255Sdelphij		if (rflag)
1977166255Sdelphij			handle_dir(path);
1978166255Sdelphij		else
1979166255Sdelphij#endif
1980166255Sdelphij			maybe_warnx("%s is a directory", path);
1981166255Sdelphij		goto out;
1982166255Sdelphij	}
1983166255Sdelphij
1984166255Sdelphij	if (S_ISREG(sb.st_mode))
1985166255Sdelphij		handle_file(path, &sb);
1986166255Sdelphij	else
1987166255Sdelphij		maybe_warnx("%s is not a regular file", path);
1988166255Sdelphij
1989166255Sdelphijout:
1990166255Sdelphij	if (s)
1991166255Sdelphij		free(s);
1992166255Sdelphij}
1993166255Sdelphij
1994166255Sdelphij/* compress/decompress a file */
1995166255Sdelphijstatic void
1996166255Sdelphijhandle_file(char *file, struct stat *sbp)
1997166255Sdelphij{
1998166255Sdelphij	off_t usize, gsize;
1999166255Sdelphij	char	outfile[PATH_MAX];
2000166255Sdelphij
2001326559Sdelphij	infile_set(file, sbp->st_size);
2002166255Sdelphij	if (dflag) {
2003166255Sdelphij		usize = file_uncompress(file, outfile, sizeof(outfile));
2004166255Sdelphij#ifndef SMALL
2005166255Sdelphij		if (vflag && tflag)
2006166255Sdelphij			print_test(file, usize != -1);
2007166255Sdelphij#endif
2008166255Sdelphij		if (usize == -1)
2009166255Sdelphij			return;
2010166255Sdelphij		gsize = sbp->st_size;
2011166255Sdelphij	} else {
2012166255Sdelphij		gsize = file_compress(file, outfile, sizeof(outfile));
2013166255Sdelphij		if (gsize == -1)
2014166255Sdelphij			return;
2015166255Sdelphij		usize = sbp->st_size;
2016166255Sdelphij	}
2017326559Sdelphij	infile_clear();
2018166255Sdelphij
2019166255Sdelphij#ifndef SMALL
2020166255Sdelphij	if (vflag && !tflag)
2021166255Sdelphij		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
2022166255Sdelphij#endif
2023166255Sdelphij}
2024166255Sdelphij
2025166255Sdelphij#ifndef SMALL
2026166255Sdelphij/* this is used with -r to recursively descend directories */
2027166255Sdelphijstatic void
2028166255Sdelphijhandle_dir(char *dir)
2029166255Sdelphij{
2030166255Sdelphij	char *path_argv[2];
2031166255Sdelphij	FTS *fts;
2032166255Sdelphij	FTSENT *entry;
2033166255Sdelphij
2034166255Sdelphij	path_argv[0] = dir;
2035166255Sdelphij	path_argv[1] = 0;
2036171389Sdelphij	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
2037166255Sdelphij	if (fts == NULL) {
2038166255Sdelphij		warn("couldn't fts_open %s", dir);
2039166255Sdelphij		return;
2040166255Sdelphij	}
2041166255Sdelphij
2042166255Sdelphij	while ((entry = fts_read(fts))) {
2043166255Sdelphij		switch(entry->fts_info) {
2044166255Sdelphij		case FTS_D:
2045166255Sdelphij		case FTS_DP:
2046166255Sdelphij			continue;
2047166255Sdelphij
2048166255Sdelphij		case FTS_DNR:
2049166255Sdelphij		case FTS_ERR:
2050166255Sdelphij		case FTS_NS:
2051166255Sdelphij			maybe_warn("%s", entry->fts_path);
2052166255Sdelphij			continue;
2053166255Sdelphij		case FTS_F:
2054171389Sdelphij			handle_file(entry->fts_path, entry->fts_statp);
2055166255Sdelphij		}
2056166255Sdelphij	}
2057166255Sdelphij	(void)fts_close(fts);
2058166255Sdelphij}
2059166255Sdelphij#endif
2060166255Sdelphij
2061166255Sdelphij/* print a ratio - size reduction as a fraction of uncompressed size */
2062166255Sdelphijstatic void
2063166255Sdelphijprint_ratio(off_t in, off_t out, FILE *where)
2064166255Sdelphij{
2065166255Sdelphij	int percent10;	/* 10 * percent */
2066166255Sdelphij	off_t diff;
2067166255Sdelphij	char buff[8];
2068166255Sdelphij	int len;
2069166255Sdelphij
2070166255Sdelphij	diff = in - out/2;
2071326559Sdelphij	if (in == 0 && out == 0)
2072326559Sdelphij		percent10 = 0;
2073326559Sdelphij	else if (diff < 0)
2074166255Sdelphij		/*
2075166255Sdelphij		 * Output is more than double size of input! print -99.9%
2076166255Sdelphij		 * Quite possibly we've failed to get the original size.
2077166255Sdelphij		 */
2078166255Sdelphij		percent10 = -999;
2079166255Sdelphij	else {
2080166255Sdelphij		/*
2081166255Sdelphij		 * We only need 12 bits of result from the final division,
2082166255Sdelphij		 * so reduce the values until a 32bit division will suffice.
2083166255Sdelphij		 */
2084166255Sdelphij		while (in > 0x100000) {
2085166255Sdelphij			diff >>= 1;
2086166255Sdelphij			in >>= 1;
2087166255Sdelphij		}
2088166255Sdelphij		if (in != 0)
2089166255Sdelphij			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2090166255Sdelphij		else
2091166255Sdelphij			percent10 = 0;
2092166255Sdelphij	}
2093166255Sdelphij
2094166255Sdelphij	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2095166255Sdelphij	/* Move the '.' to before the last digit */
2096166255Sdelphij	buff[len - 1] = buff[len - 2];
2097166255Sdelphij	buff[len - 2] = '.';
2098166255Sdelphij	fprintf(where, "%5s%%", buff);
2099166255Sdelphij}
2100166255Sdelphij
2101166255Sdelphij#ifndef SMALL
2102166255Sdelphij/* print compression statistics, and the new name (if there is one!) */
2103166255Sdelphijstatic void
2104166255Sdelphijprint_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
2105166255Sdelphij{
2106166255Sdelphij	if (file)
2107166255Sdelphij		fprintf(stderr, "%s:%s  ", file,
2108166255Sdelphij		    strlen(file) < 7 ? "\t\t" : "\t");
2109166255Sdelphij	print_ratio(usize, gsize, stderr);
2110166255Sdelphij	if (nfile)
2111166255Sdelphij		fprintf(stderr, " -- replaced with %s", nfile);
2112166255Sdelphij	fprintf(stderr, "\n");
2113166255Sdelphij	fflush(stderr);
2114166255Sdelphij}
2115166255Sdelphij
2116166255Sdelphij/* print test results */
2117166255Sdelphijstatic void
2118166255Sdelphijprint_test(const char *file, int ok)
2119166255Sdelphij{
2120166255Sdelphij
2121166255Sdelphij	if (exit_value == 0 && ok == 0)
2122166255Sdelphij		exit_value = 1;
2123166255Sdelphij	fprintf(stderr, "%s:%s  %s\n", file,
2124166255Sdelphij	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2125166255Sdelphij	fflush(stderr);
2126166255Sdelphij}
2127166255Sdelphij#endif
2128166255Sdelphij
2129166255Sdelphij/* print a file's info ala --list */
2130166255Sdelphij/* eg:
2131166255Sdelphij  compressed uncompressed  ratio uncompressed_name
2132166255Sdelphij      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2133166255Sdelphij*/
2134166255Sdelphijstatic void
2135166255Sdelphijprint_list(int fd, off_t out, const char *outfile, time_t ts)
2136166255Sdelphij{
2137166255Sdelphij	static int first = 1;
2138166255Sdelphij#ifndef SMALL
2139166255Sdelphij	static off_t in_tot, out_tot;
2140166255Sdelphij	uint32_t crc = 0;
2141166255Sdelphij#endif
2142166255Sdelphij	off_t in = 0, rv;
2143166255Sdelphij
2144166255Sdelphij	if (first) {
2145166255Sdelphij#ifndef SMALL
2146166255Sdelphij		if (vflag)
2147166255Sdelphij			printf("method  crc     date  time  ");
2148166255Sdelphij#endif
2149166255Sdelphij		if (qflag == 0)
2150166255Sdelphij			printf("  compressed uncompressed  "
2151166255Sdelphij			       "ratio uncompressed_name\n");
2152166255Sdelphij	}
2153166255Sdelphij	first = 0;
2154166255Sdelphij
2155166255Sdelphij	/* print totals? */
2156166255Sdelphij#ifndef SMALL
2157166255Sdelphij	if (fd == -1) {
2158166255Sdelphij		in = in_tot;
2159166255Sdelphij		out = out_tot;
2160166255Sdelphij	} else
2161166255Sdelphij#endif
2162166255Sdelphij	{
2163166255Sdelphij		/* read the last 4 bytes - this is the uncompressed size */
2164166255Sdelphij		rv = lseek(fd, (off_t)(-8), SEEK_END);
2165166255Sdelphij		if (rv != -1) {
2166166255Sdelphij			unsigned char buf[8];
2167166255Sdelphij			uint32_t usize;
2168166255Sdelphij
2169166255Sdelphij			rv = read(fd, (char *)buf, sizeof(buf));
2170166255Sdelphij			if (rv == -1)
2171166255Sdelphij				maybe_warn("read of uncompressed size");
2172166255Sdelphij			else if (rv != sizeof(buf))
2173166255Sdelphij				maybe_warnx("read of uncompressed size");
2174166255Sdelphij
2175166255Sdelphij			else {
2176336661Sdelphij				usize = le32dec(&buf[4]);
2177166255Sdelphij				in = (off_t)usize;
2178166255Sdelphij#ifndef SMALL
2179336661Sdelphij				crc = le32dec(&buf[0]);
2180166255Sdelphij#endif
2181166255Sdelphij			}
2182166255Sdelphij		}
2183166255Sdelphij	}
2184166255Sdelphij
2185166255Sdelphij#ifndef SMALL
2186166255Sdelphij	if (vflag && fd == -1)
2187166255Sdelphij		printf("                            ");
2188166255Sdelphij	else if (vflag) {
2189166255Sdelphij		char *date = ctime(&ts);
2190166255Sdelphij
2191166255Sdelphij		/* skip the day, 1/100th second, and year */
2192166255Sdelphij		date += 4;
2193166255Sdelphij		date[12] = 0;
2194166255Sdelphij		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2195166255Sdelphij	}
2196166255Sdelphij	in_tot += in;
2197166255Sdelphij	out_tot += out;
2198194508Sdelphij#else
2199194508Sdelphij	(void)&ts;	/* XXX */
2200166255Sdelphij#endif
2201166255Sdelphij	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2202166255Sdelphij	print_ratio(in, out, stdout);
2203166255Sdelphij	printf(" %s\n", outfile);
2204166255Sdelphij}
2205166255Sdelphij
2206166255Sdelphij/* display the usage of NetBSD gzip */
2207166255Sdelphijstatic void
2208166255Sdelphijusage(void)
2209166255Sdelphij{
2210166255Sdelphij
2211166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2212166255Sdelphij	fprintf(stderr,
2213171389Sdelphij#ifdef SMALL
2214171389Sdelphij    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2215171389Sdelphij#else
2216171389Sdelphij    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2217166255Sdelphij    " -1 --fast            fastest (worst) compression\n"
2218166255Sdelphij    " -2 .. -8             set compression level\n"
2219166255Sdelphij    " -9 --best            best (slowest) compression\n"
2220166255Sdelphij    " -c --stdout          write to stdout, keep original files\n"
2221166255Sdelphij    "    --to-stdout\n"
2222166255Sdelphij    " -d --decompress      uncompress files\n"
2223166255Sdelphij    "    --uncompress\n"
2224166255Sdelphij    " -f --force           force overwriting & compress links\n"
2225166255Sdelphij    " -h --help            display this help\n"
2226170053Sdelphij    " -k --keep            don't delete input files during operation\n"
2227166255Sdelphij    " -l --list            list compressed file contents\n"
2228166255Sdelphij    " -N --name            save or restore original file name and time stamp\n"
2229166255Sdelphij    " -n --no-name         don't save original file name or time stamp\n"
2230166255Sdelphij    " -q --quiet           output no warnings\n"
2231166255Sdelphij    " -r --recursive       recursively compress files in directories\n"
2232166255Sdelphij    " -S .suf              use suffix .suf instead of .gz\n"
2233166255Sdelphij    "    --suffix .suf\n"
2234166255Sdelphij    " -t --test            test compressed file\n"
2235166255Sdelphij    " -V --version         display program version\n"
2236166255Sdelphij    " -v --verbose         print extra statistics\n",
2237166255Sdelphij#endif
2238166255Sdelphij	    getprogname());
2239166255Sdelphij	exit(0);
2240166255Sdelphij}
2241166255Sdelphij
2242166255Sdelphij#ifndef SMALL
2243166255Sdelphij/* display the license information of FreeBSD gzip */
2244166255Sdelphijstatic void
2245166255Sdelphijdisplay_license(void)
2246166255Sdelphij{
2247166255Sdelphij
2248281500Sdelphij	fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
2249166255Sdelphij	fprintf(stderr, "%s\n", gzip_copyright);
2250166255Sdelphij	exit(0);
2251166255Sdelphij}
2252166255Sdelphij#endif
2253166255Sdelphij
2254166255Sdelphij/* display the version of NetBSD gzip */
2255166255Sdelphijstatic void
2256166255Sdelphijdisplay_version(void)
2257166255Sdelphij{
2258166255Sdelphij
2259166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2260166255Sdelphij	exit(0);
2261166255Sdelphij}
2262166255Sdelphij
2263166255Sdelphij#ifndef NO_BZIP2_SUPPORT
2264166255Sdelphij#include "unbzip2.c"
2265166255Sdelphij#endif
2266166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
2267166255Sdelphij#include "zuncompress.c"
2268166255Sdelphij#endif
2269194579Sdelphij#ifndef NO_PACK_SUPPORT
2270194579Sdelphij#include "unpack.c"
2271194579Sdelphij#endif
2272226184Sdelphij#ifndef NO_XZ_SUPPORT
2273226184Sdelphij#include "unxz.c"
2274226184Sdelphij#endif
2275166255Sdelphij
2276166255Sdelphijstatic ssize_t
2277166255Sdelphijread_retry(int fd, void *buf, size_t sz)
2278166255Sdelphij{
2279166255Sdelphij	char *cp = buf;
2280166255Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2281166255Sdelphij
2282166255Sdelphij	while (left > 0) {
2283166255Sdelphij		ssize_t ret;
2284166255Sdelphij
2285166255Sdelphij		ret = read(fd, cp, left);
2286166255Sdelphij		if (ret == -1) {
2287166255Sdelphij			return ret;
2288166255Sdelphij		} else if (ret == 0) {
2289166255Sdelphij			break; /* EOF */
2290166255Sdelphij		}
2291166255Sdelphij		cp += ret;
2292166255Sdelphij		left -= ret;
2293166255Sdelphij	}
2294166255Sdelphij
2295166255Sdelphij	return sz - left;
2296166255Sdelphij}
2297326559Sdelphij
2298326559Sdelphijstatic ssize_t
2299326559Sdelphijwrite_retry(int fd, const void *buf, size_t sz)
2300326559Sdelphij{
2301326559Sdelphij	const char *cp = buf;
2302326559Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2303326559Sdelphij
2304326559Sdelphij	while (left > 0) {
2305326559Sdelphij		ssize_t ret;
2306326559Sdelphij
2307326559Sdelphij		ret = write(fd, cp, left);
2308326559Sdelphij		if (ret == -1) {
2309326559Sdelphij			return ret;
2310326559Sdelphij		} else if (ret == 0) {
2311326559Sdelphij			abort();	/* Can't happen */
2312326559Sdelphij		}
2313326559Sdelphij		cp += ret;
2314326559Sdelphij		left -= ret;
2315326559Sdelphij	}
2316326559Sdelphij
2317326559Sdelphij	return sz - left;
2318326559Sdelphij}
2319