1343251Sdelphij/*	$NetBSD: gzip.c,v 1.116 2018/10/27 11:39:12 skrll 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 343251 2019-01-21 06:52:35Z 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
87343251Sdelphij#ifndef NO_LZ_SUPPORT
88343251Sdelphij	FT_LZ,
89343251Sdelphij#endif
90166255Sdelphij	FT_LAST,
91166255Sdelphij	FT_UNKNOWN
92166255Sdelphij};
93166255Sdelphij
94166255Sdelphij#ifndef NO_BZIP2_SUPPORT
95166255Sdelphij#include <bzlib.h>
96166255Sdelphij
97166255Sdelphij#define BZ2_SUFFIX	".bz2"
98309850Sdelphij#define BZIP2_MAGIC	"BZh"
99166255Sdelphij#endif
100166255Sdelphij
101166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
102166255Sdelphij#define Z_SUFFIX	".Z"
103166255Sdelphij#define Z_MAGIC		"\037\235"
104166255Sdelphij#endif
105166255Sdelphij
106194579Sdelphij#ifndef NO_PACK_SUPPORT
107194579Sdelphij#define PACK_MAGIC	"\037\036"
108194579Sdelphij#endif
109194579Sdelphij
110226184Sdelphij#ifndef NO_XZ_SUPPORT
111226184Sdelphij#include <lzma.h>
112226184Sdelphij#define XZ_SUFFIX	".xz"
113226184Sdelphij#define XZ_MAGIC	"\3757zXZ"
114226184Sdelphij#endif
115226184Sdelphij
116343251Sdelphij#ifndef NO_LZ_SUPPORT
117343251Sdelphij#define LZ_SUFFIX	".lz"
118343251Sdelphij#define LZ_MAGIC	"LZIP"
119343251Sdelphij#endif
120343251Sdelphij
121166255Sdelphij#define GZ_SUFFIX	".gz"
122166255Sdelphij
123166255Sdelphij#define BUFLEN		(64 * 1024)
124166255Sdelphij
125166255Sdelphij#define GZIP_MAGIC0	0x1F
126166255Sdelphij#define GZIP_MAGIC1	0x8B
127166255Sdelphij#define GZIP_OMAGIC1	0x9E
128166255Sdelphij
129166255Sdelphij#define GZIP_TIMESTAMP	(off_t)4
130166255Sdelphij#define GZIP_ORIGNAME	(off_t)10
131166255Sdelphij
132166255Sdelphij#define HEAD_CRC	0x02
133166255Sdelphij#define EXTRA_FIELD	0x04
134166255Sdelphij#define ORIG_NAME	0x08
135166255Sdelphij#define COMMENT		0x10
136166255Sdelphij
137166255Sdelphij#define OS_CODE		3	/* Unix */
138166255Sdelphij
139166255Sdelphijtypedef struct {
140166255Sdelphij    const char	*zipped;
141166255Sdelphij    int		ziplen;
142166255Sdelphij    const char	*normal;	/* for unzip - must not be longer than zipped */
143166255Sdelphij} suffixes_t;
144166255Sdelphijstatic suffixes_t suffixes[] = {
145166255Sdelphij#define	SUFFIX(Z, N) {Z, sizeof Z - 1, N}
146166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S .xxx */
147166255Sdelphij#ifndef SMALL
148166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),
149166255Sdelphij	SUFFIX(".z",		""),
150166255Sdelphij	SUFFIX("-gz",		""),
151166255Sdelphij	SUFFIX("-z",		""),
152166255Sdelphij	SUFFIX("_z",		""),
153166255Sdelphij	SUFFIX(".taz",		".tar"),
154166255Sdelphij	SUFFIX(".tgz",		".tar"),
155166255Sdelphij#ifndef NO_BZIP2_SUPPORT
156166255Sdelphij	SUFFIX(BZ2_SUFFIX,	""),
157176980Srwatson	SUFFIX(".tbz",		".tar"),
158176980Srwatson	SUFFIX(".tbz2",		".tar"),
159166255Sdelphij#endif
160166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
161166255Sdelphij	SUFFIX(Z_SUFFIX,	""),
162166255Sdelphij#endif
163226184Sdelphij#ifndef NO_XZ_SUPPORT
164226184Sdelphij	SUFFIX(XZ_SUFFIX,	""),
165226184Sdelphij#endif
166343251Sdelphij#ifndef NO_LZ_SUPPORT
167343251Sdelphij	SUFFIX(LZ_SUFFIX,	""),
168343251Sdelphij#endif
169166255Sdelphij	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S "" */
170166255Sdelphij#endif /* SMALL */
171166255Sdelphij#undef SUFFIX
172166255Sdelphij};
173307697Saraujo#define NUM_SUFFIXES (nitems(suffixes))
174195988Sdelphij#define SUFFIX_MAXLEN	30
175195988Sdelphij
176343251Sdelphijstatic	const char	gzip_version[] = "FreeBSD gzip 20190107";
177166255Sdelphij
178166255Sdelphij#ifndef SMALL
179166255Sdelphijstatic	const char	gzip_copyright[] = \
180166255Sdelphij"   Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
181166255Sdelphij"   All rights reserved.\n"
182166255Sdelphij"\n"
183166255Sdelphij"   Redistribution and use in source and binary forms, with or without\n"
184166255Sdelphij"   modification, are permitted provided that the following conditions\n"
185166255Sdelphij"   are met:\n"
186166255Sdelphij"   1. Redistributions of source code must retain the above copyright\n"
187166255Sdelphij"      notice, this list of conditions and the following disclaimer.\n"
188166255Sdelphij"   2. Redistributions in binary form must reproduce the above copyright\n"
189166255Sdelphij"      notice, this list of conditions and the following disclaimer in the\n"
190166255Sdelphij"      documentation and/or other materials provided with the distribution.\n"
191166255Sdelphij"\n"
192166255Sdelphij"   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
193166255Sdelphij"   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
194166255Sdelphij"   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
195166255Sdelphij"   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
196166255Sdelphij"   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
197166255Sdelphij"   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
198166255Sdelphij"   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
199166255Sdelphij"   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
200166255Sdelphij"   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
201166255Sdelphij"   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
202166255Sdelphij"   SUCH DAMAGE.";
203166255Sdelphij#endif
204166255Sdelphij
205166255Sdelphijstatic	int	cflag;			/* stdout mode */
206166255Sdelphijstatic	int	dflag;			/* decompress mode */
207166255Sdelphijstatic	int	lflag;			/* list mode */
208166255Sdelphijstatic	int	numflag = 6;		/* gzip -1..-9 value */
209166255Sdelphij
210326559Sdelphijstatic	const char *remove_file = NULL;	/* file to be removed upon SIGINT */
211326559Sdelphij
212326559Sdelphijstatic	int	fflag;			/* force mode */
213166255Sdelphij#ifndef SMALL
214170053Sdelphijstatic	int	kflag;			/* don't delete input files */
215166255Sdelphijstatic	int	nflag;			/* don't save name/timestamp */
216166255Sdelphijstatic	int	Nflag;			/* don't restore name/timestamp */
217166255Sdelphijstatic	int	qflag;			/* quiet mode */
218166255Sdelphijstatic	int	rflag;			/* recursive mode */
219166255Sdelphijstatic	int	tflag;			/* test */
220166255Sdelphijstatic	int	vflag;			/* verbose mode */
221326559Sdelphijstatic	sig_atomic_t print_info = 0;
222166255Sdelphij#else
223166255Sdelphij#define		qflag	0
224166255Sdelphij#define		tflag	0
225166255Sdelphij#endif
226166255Sdelphij
227166255Sdelphijstatic	int	exit_value = 0;		/* exit value */
228166255Sdelphij
229326559Sdelphijstatic	const char *infile;		/* name of file coming in */
230166255Sdelphij
231226184Sdelphijstatic	void	maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
232226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
233226184Sdelphij    !defined(NO_XZ_SUPPORT)
234226184Sdelphijstatic	void	maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
235166255Sdelphij#endif
236226184Sdelphijstatic	void	maybe_warn(const char *fmt, ...) __printflike(1, 2);
237226184Sdelphijstatic	void	maybe_warnx(const char *fmt, ...) __printflike(1, 2);
238166255Sdelphijstatic	enum filetype file_gettype(u_char *);
239166255Sdelphij#ifdef SMALL
240166255Sdelphij#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
241166255Sdelphij#endif
242166255Sdelphijstatic	off_t	gz_compress(int, int, off_t *, const char *, uint32_t);
243166255Sdelphijstatic	off_t	gz_uncompress(int, int, char *, size_t, off_t *, const char *);
244166255Sdelphijstatic	off_t	file_compress(char *, char *, size_t);
245166255Sdelphijstatic	off_t	file_uncompress(char *, char *, size_t);
246166255Sdelphijstatic	void	handle_pathname(char *);
247166255Sdelphijstatic	void	handle_file(char *, struct stat *);
248166255Sdelphijstatic	void	handle_stdin(void);
249166255Sdelphijstatic	void	handle_stdout(void);
250166255Sdelphijstatic	void	print_ratio(off_t, off_t, FILE *);
251166255Sdelphijstatic	void	print_list(int fd, off_t, const char *, time_t);
252226184Sdelphijstatic	void	usage(void) __dead2;
253226184Sdelphijstatic	void	display_version(void) __dead2;
254166255Sdelphij#ifndef SMALL
255166255Sdelphijstatic	void	display_license(void);
256166255Sdelphij#endif
257166255Sdelphijstatic	const suffixes_t *check_suffix(char *, int);
258166255Sdelphijstatic	ssize_t	read_retry(int, void *, size_t);
259326559Sdelphijstatic	ssize_t	write_retry(int, const void *, size_t);
260343251Sdelphijstatic void	print_list_out(off_t, off_t, const char*);
261166255Sdelphij
262166255Sdelphij#ifdef SMALL
263326559Sdelphij#define infile_set(f,t) infile_set(f)
264326559Sdelphij#endif
265326559Sdelphijstatic	void	infile_set(const char *newinfile, off_t total);
266326559Sdelphij
267326559Sdelphij#ifdef SMALL
268166255Sdelphij#define unlink_input(f, sb) unlink(f)
269326559Sdelphij#define check_siginfo() /* nothing */
270326559Sdelphij#define setup_signals() /* nothing */
271326559Sdelphij#define infile_newdata(t) /* nothing */
272166255Sdelphij#else
273326559Sdelphijstatic	off_t	infile_total;		/* total expected to read/write */
274326559Sdelphijstatic	off_t	infile_current;		/* current read/write */
275326559Sdelphij
276326559Sdelphijstatic	void	check_siginfo(void);
277166255Sdelphijstatic	off_t	cat_fd(unsigned char *, size_t, off_t *, int fd);
278166255Sdelphijstatic	void	prepend_gzip(char *, int *, char ***);
279166255Sdelphijstatic	void	handle_dir(char *);
280166255Sdelphijstatic	void	print_verbage(const char *, const char *, off_t, off_t);
281166255Sdelphijstatic	void	print_test(const char *, int);
282166255Sdelphijstatic	void	copymodes(int fd, const struct stat *, const char *file);
283166255Sdelphijstatic	int	check_outfile(const char *outfile);
284326559Sdelphijstatic	void	setup_signals(void);
285326559Sdelphijstatic	void	infile_newdata(size_t newdata);
286326559Sdelphijstatic	void	infile_clear(void);
287166255Sdelphij#endif
288166255Sdelphij
289166255Sdelphij#ifndef NO_BZIP2_SUPPORT
290166255Sdelphijstatic	off_t	unbzip2(int, int, char *, size_t, off_t *);
291166255Sdelphij#endif
292166255Sdelphij
293166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
294166255Sdelphijstatic	FILE 	*zdopen(int);
295166255Sdelphijstatic	off_t	zuncompress(FILE *, FILE *, char *, size_t, off_t *);
296166255Sdelphij#endif
297166255Sdelphij
298194579Sdelphij#ifndef NO_PACK_SUPPORT
299194579Sdelphijstatic	off_t	unpack(int, int, char *, size_t, off_t *);
300194579Sdelphij#endif
301194579Sdelphij
302226184Sdelphij#ifndef NO_XZ_SUPPORT
303226184Sdelphijstatic	off_t	unxz(int, int, char *, size_t, off_t *);
304343251Sdelphijstatic	off_t	unxz_len(int);
305226184Sdelphij#endif
306166255Sdelphij
307343251Sdelphij#ifndef NO_LZ_SUPPORT
308343251Sdelphijstatic	off_t	unlz(int, int, char *, size_t, off_t *);
309343251Sdelphij#endif
310343251Sdelphij
311166255Sdelphij#ifdef SMALL
312166255Sdelphij#define getopt_long(a,b,c,d,e) getopt(a,b,c)
313166255Sdelphij#else
314166255Sdelphijstatic const struct option longopts[] = {
315166255Sdelphij	{ "stdout",		no_argument,		0,	'c' },
316166255Sdelphij	{ "to-stdout",		no_argument,		0,	'c' },
317166255Sdelphij	{ "decompress",		no_argument,		0,	'd' },
318166255Sdelphij	{ "uncompress",		no_argument,		0,	'd' },
319166255Sdelphij	{ "force",		no_argument,		0,	'f' },
320166255Sdelphij	{ "help",		no_argument,		0,	'h' },
321170053Sdelphij	{ "keep",		no_argument,		0,	'k' },
322166255Sdelphij	{ "list",		no_argument,		0,	'l' },
323166255Sdelphij	{ "no-name",		no_argument,		0,	'n' },
324166255Sdelphij	{ "name",		no_argument,		0,	'N' },
325166255Sdelphij	{ "quiet",		no_argument,		0,	'q' },
326166255Sdelphij	{ "recursive",		no_argument,		0,	'r' },
327166255Sdelphij	{ "suffix",		required_argument,	0,	'S' },
328166255Sdelphij	{ "test",		no_argument,		0,	't' },
329166255Sdelphij	{ "verbose",		no_argument,		0,	'v' },
330166255Sdelphij	{ "version",		no_argument,		0,	'V' },
331166255Sdelphij	{ "fast",		no_argument,		0,	'1' },
332166255Sdelphij	{ "best",		no_argument,		0,	'9' },
333166255Sdelphij	{ "ascii",		no_argument,		0,	'a' },
334166255Sdelphij	{ "license",		no_argument,		0,	'L' },
335166255Sdelphij	{ NULL,			no_argument,		0,	0 },
336166255Sdelphij};
337166255Sdelphij#endif
338166255Sdelphij
339166255Sdelphijint
340166255Sdelphijmain(int argc, char **argv)
341166255Sdelphij{
342166255Sdelphij	const char *progname = getprogname();
343166255Sdelphij#ifndef SMALL
344166255Sdelphij	char *gzip;
345166255Sdelphij	int len;
346166255Sdelphij#endif
347166255Sdelphij	int ch;
348166255Sdelphij
349326559Sdelphij	setup_signals();
350326559Sdelphij
351166255Sdelphij#ifndef SMALL
352166255Sdelphij	if ((gzip = getenv("GZIP")) != NULL)
353166255Sdelphij		prepend_gzip(gzip, &argc, &argv);
354166255Sdelphij#endif
355166255Sdelphij
356166255Sdelphij	/*
357166255Sdelphij	 * XXX
358166255Sdelphij	 * handle being called `gunzip', `zcat' and `gzcat'
359166255Sdelphij	 */
360166255Sdelphij	if (strcmp(progname, "gunzip") == 0)
361166255Sdelphij		dflag = 1;
362166255Sdelphij	else if (strcmp(progname, "zcat") == 0 ||
363166255Sdelphij		 strcmp(progname, "gzcat") == 0)
364166255Sdelphij		dflag = cflag = 1;
365166255Sdelphij
366166255Sdelphij#ifdef SMALL
367222210Sdelphij#define OPT_LIST "123456789cdhlV"
368166255Sdelphij#else
369170053Sdelphij#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
370166255Sdelphij#endif
371166255Sdelphij
372166255Sdelphij	while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
373166255Sdelphij		switch (ch) {
374166255Sdelphij		case '1': case '2': case '3':
375166255Sdelphij		case '4': case '5': case '6':
376166255Sdelphij		case '7': case '8': case '9':
377166255Sdelphij			numflag = ch - '0';
378166255Sdelphij			break;
379166255Sdelphij		case 'c':
380166255Sdelphij			cflag = 1;
381166255Sdelphij			break;
382166255Sdelphij		case 'd':
383166255Sdelphij			dflag = 1;
384166255Sdelphij			break;
385166255Sdelphij		case 'l':
386166255Sdelphij			lflag = 1;
387166255Sdelphij			dflag = 1;
388166255Sdelphij			break;
389166255Sdelphij		case 'V':
390166255Sdelphij			display_version();
391166255Sdelphij			/* NOTREACHED */
392166255Sdelphij#ifndef SMALL
393166255Sdelphij		case 'a':
394166255Sdelphij			fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
395166255Sdelphij			break;
396166255Sdelphij		case 'f':
397166255Sdelphij			fflag = 1;
398166255Sdelphij			break;
399170053Sdelphij		case 'k':
400170053Sdelphij			kflag = 1;
401170053Sdelphij			break;
402166255Sdelphij		case 'L':
403166255Sdelphij			display_license();
404166255Sdelphij			/* NOT REACHED */
405166255Sdelphij		case 'N':
406166255Sdelphij			nflag = 0;
407166255Sdelphij			Nflag = 1;
408166255Sdelphij			break;
409166255Sdelphij		case 'n':
410166255Sdelphij			nflag = 1;
411166255Sdelphij			Nflag = 0;
412166255Sdelphij			break;
413166255Sdelphij		case 'q':
414166255Sdelphij			qflag = 1;
415166255Sdelphij			break;
416166255Sdelphij		case 'r':
417166255Sdelphij			rflag = 1;
418166255Sdelphij			break;
419166255Sdelphij		case 'S':
420166255Sdelphij			len = strlen(optarg);
421166255Sdelphij			if (len != 0) {
422195988Sdelphij				if (len > SUFFIX_MAXLEN)
423195988Sdelphij					errx(1, "incorrect suffix: '%s': too long", optarg);
424166255Sdelphij				suffixes[0].zipped = optarg;
425166255Sdelphij				suffixes[0].ziplen = len;
426166255Sdelphij			} else {
427166255Sdelphij				suffixes[NUM_SUFFIXES - 1].zipped = "";
428166255Sdelphij				suffixes[NUM_SUFFIXES - 1].ziplen = 0;
429166255Sdelphij			}
430166255Sdelphij			break;
431166255Sdelphij		case 't':
432166255Sdelphij			cflag = 1;
433166255Sdelphij			tflag = 1;
434166255Sdelphij			dflag = 1;
435166255Sdelphij			break;
436166255Sdelphij		case 'v':
437166255Sdelphij			vflag = 1;
438166255Sdelphij			break;
439166255Sdelphij#endif
440166255Sdelphij		default:
441166255Sdelphij			usage();
442166255Sdelphij			/* NOTREACHED */
443166255Sdelphij		}
444166255Sdelphij	}
445166255Sdelphij	argv += optind;
446166255Sdelphij	argc -= optind;
447166255Sdelphij
448166255Sdelphij	if (argc == 0) {
449166255Sdelphij		if (dflag)	/* stdin mode */
450166255Sdelphij			handle_stdin();
451166255Sdelphij		else		/* stdout mode */
452166255Sdelphij			handle_stdout();
453166255Sdelphij	} else {
454166255Sdelphij		do {
455166255Sdelphij			handle_pathname(argv[0]);
456166255Sdelphij		} while (*++argv);
457166255Sdelphij	}
458166255Sdelphij#ifndef SMALL
459166255Sdelphij	if (qflag == 0 && lflag && argc > 1)
460166255Sdelphij		print_list(-1, 0, "(totals)", 0);
461166255Sdelphij#endif
462166255Sdelphij	exit(exit_value);
463166255Sdelphij}
464166255Sdelphij
465166255Sdelphij/* maybe print a warning */
466166255Sdelphijvoid
467166255Sdelphijmaybe_warn(const char *fmt, ...)
468166255Sdelphij{
469166255Sdelphij	va_list ap;
470166255Sdelphij
471166255Sdelphij	if (qflag == 0) {
472166255Sdelphij		va_start(ap, fmt);
473166255Sdelphij		vwarn(fmt, ap);
474166255Sdelphij		va_end(ap);
475166255Sdelphij	}
476166255Sdelphij	if (exit_value == 0)
477166255Sdelphij		exit_value = 1;
478166255Sdelphij}
479166255Sdelphij
480166255Sdelphij/* ... without an errno. */
481166255Sdelphijvoid
482166255Sdelphijmaybe_warnx(const char *fmt, ...)
483166255Sdelphij{
484166255Sdelphij	va_list ap;
485166255Sdelphij
486166255Sdelphij	if (qflag == 0) {
487166255Sdelphij		va_start(ap, fmt);
488166255Sdelphij		vwarnx(fmt, ap);
489166255Sdelphij		va_end(ap);
490166255Sdelphij	}
491166255Sdelphij	if (exit_value == 0)
492166255Sdelphij		exit_value = 1;
493166255Sdelphij}
494166255Sdelphij
495166255Sdelphij/* maybe print an error */
496166255Sdelphijvoid
497166255Sdelphijmaybe_err(const char *fmt, ...)
498166255Sdelphij{
499166255Sdelphij	va_list ap;
500166255Sdelphij
501166255Sdelphij	if (qflag == 0) {
502166255Sdelphij		va_start(ap, fmt);
503166255Sdelphij		vwarn(fmt, ap);
504166255Sdelphij		va_end(ap);
505166255Sdelphij	}
506166255Sdelphij	exit(2);
507166255Sdelphij}
508166255Sdelphij
509226184Sdelphij#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
510226184Sdelphij    !defined(NO_XZ_SUPPORT)
511166255Sdelphij/* ... without an errno. */
512166255Sdelphijvoid
513166255Sdelphijmaybe_errx(const char *fmt, ...)
514166255Sdelphij{
515166255Sdelphij	va_list ap;
516166255Sdelphij
517166255Sdelphij	if (qflag == 0) {
518166255Sdelphij		va_start(ap, fmt);
519166255Sdelphij		vwarnx(fmt, ap);
520166255Sdelphij		va_end(ap);
521166255Sdelphij	}
522166255Sdelphij	exit(2);
523166255Sdelphij}
524166255Sdelphij#endif
525166255Sdelphij
526166255Sdelphij#ifndef SMALL
527166255Sdelphij/* split up $GZIP and prepend it to the argument list */
528166255Sdelphijstatic void
529166255Sdelphijprepend_gzip(char *gzip, int *argc, char ***argv)
530166255Sdelphij{
531166255Sdelphij	char *s, **nargv, **ac;
532166255Sdelphij	int nenvarg = 0, i;
533166255Sdelphij
534166255Sdelphij	/* scan how many arguments there are */
535166255Sdelphij	for (s = gzip;;) {
536166255Sdelphij		while (*s == ' ' || *s == '\t')
537166255Sdelphij			s++;
538166255Sdelphij		if (*s == 0)
539166255Sdelphij			goto count_done;
540166255Sdelphij		nenvarg++;
541166255Sdelphij		while (*s != ' ' && *s != '\t')
542166255Sdelphij			if (*s++ == 0)
543166255Sdelphij				goto count_done;
544166255Sdelphij	}
545166255Sdelphijcount_done:
546166255Sdelphij	/* punt early */
547166255Sdelphij	if (nenvarg == 0)
548166255Sdelphij		return;
549166255Sdelphij
550166255Sdelphij	*argc += nenvarg;
551166255Sdelphij	ac = *argv;
552166255Sdelphij
553166255Sdelphij	nargv = (char **)malloc((*argc + 1) * sizeof(char *));
554166255Sdelphij	if (nargv == NULL)
555166255Sdelphij		maybe_err("malloc");
556166255Sdelphij
557166255Sdelphij	/* stash this away */
558166255Sdelphij	*argv = nargv;
559166255Sdelphij
560166255Sdelphij	/* copy the program name first */
561166255Sdelphij	i = 0;
562166255Sdelphij	nargv[i++] = *(ac++);
563166255Sdelphij
564166255Sdelphij	/* take a copy of $GZIP and add it to the array */
565166255Sdelphij	s = strdup(gzip);
566166255Sdelphij	if (s == NULL)
567166255Sdelphij		maybe_err("strdup");
568166255Sdelphij	for (;;) {
569166255Sdelphij		/* Skip whitespaces. */
570166255Sdelphij		while (*s == ' ' || *s == '\t')
571166255Sdelphij			s++;
572166255Sdelphij		if (*s == 0)
573166255Sdelphij			goto copy_done;
574166255Sdelphij		nargv[i++] = s;
575166255Sdelphij		/* Find the end of this argument. */
576166255Sdelphij		while (*s != ' ' && *s != '\t')
577166255Sdelphij			if (*s++ == 0)
578166255Sdelphij				/* Argument followed by NUL. */
579166255Sdelphij				goto copy_done;
580166255Sdelphij		/* Terminate by overwriting ' ' or '\t' with NUL. */
581166255Sdelphij		*s++ = 0;
582166255Sdelphij	}
583166255Sdelphijcopy_done:
584166255Sdelphij
585166255Sdelphij	/* copy the original arguments and a NULL */
586166255Sdelphij	while (*ac)
587166255Sdelphij		nargv[i++] = *(ac++);
588166255Sdelphij	nargv[i] = NULL;
589166255Sdelphij}
590166255Sdelphij#endif
591166255Sdelphij
592166255Sdelphij/* compress input to output. Return bytes read, -1 on error */
593166255Sdelphijstatic off_t
594166255Sdelphijgz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
595166255Sdelphij{
596166255Sdelphij	z_stream z;
597166255Sdelphij	char *outbufp, *inbufp;
598166255Sdelphij	off_t in_tot = 0, out_tot = 0;
599166255Sdelphij	ssize_t in_size;
600166255Sdelphij	int i, error;
601166255Sdelphij	uLong crc;
602166255Sdelphij#ifdef SMALL
603166255Sdelphij	static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
604166255Sdelphij				 0, 0, 0, 0,
605166255Sdelphij				 0, OS_CODE };
606166255Sdelphij#endif
607166255Sdelphij
608166255Sdelphij	outbufp = malloc(BUFLEN);
609166255Sdelphij	inbufp = malloc(BUFLEN);
610166255Sdelphij	if (outbufp == NULL || inbufp == NULL) {
611166255Sdelphij		maybe_err("malloc failed");
612166255Sdelphij		goto out;
613166255Sdelphij	}
614166255Sdelphij
615166255Sdelphij	memset(&z, 0, sizeof z);
616166255Sdelphij	z.zalloc = Z_NULL;
617166255Sdelphij	z.zfree = Z_NULL;
618166255Sdelphij	z.opaque = 0;
619166255Sdelphij
620166255Sdelphij#ifdef SMALL
621166255Sdelphij	memcpy(outbufp, header, sizeof header);
622166255Sdelphij	i = sizeof header;
623166255Sdelphij#else
624166255Sdelphij	if (nflag != 0) {
625166255Sdelphij		mtime = 0;
626166255Sdelphij		origname = "";
627166255Sdelphij	}
628166255Sdelphij
629326559Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
630166255Sdelphij		     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
631166255Sdelphij		     *origname ? ORIG_NAME : 0,
632166255Sdelphij		     mtime & 0xff,
633166255Sdelphij		     (mtime >> 8) & 0xff,
634166255Sdelphij		     (mtime >> 16) & 0xff,
635166255Sdelphij		     (mtime >> 24) & 0xff,
636166255Sdelphij		     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
637166255Sdelphij		     OS_CODE, origname);
638326559Sdelphij	if (i >= BUFLEN)
639166255Sdelphij		/* this need PATH_MAX > BUFLEN ... */
640166255Sdelphij		maybe_err("snprintf");
641166255Sdelphij	if (*origname)
642166255Sdelphij		i++;
643166255Sdelphij#endif
644166255Sdelphij
645166255Sdelphij	z.next_out = (unsigned char *)outbufp + i;
646166255Sdelphij	z.avail_out = BUFLEN - i;
647166255Sdelphij
648166255Sdelphij	error = deflateInit2(&z, numflag, Z_DEFLATED,
649166255Sdelphij			     (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
650166255Sdelphij	if (error != Z_OK) {
651166255Sdelphij		maybe_warnx("deflateInit2 failed");
652166255Sdelphij		in_tot = -1;
653166255Sdelphij		goto out;
654166255Sdelphij	}
655166255Sdelphij
656166255Sdelphij	crc = crc32(0L, Z_NULL, 0);
657166255Sdelphij	for (;;) {
658166255Sdelphij		if (z.avail_out == 0) {
659326559Sdelphij			if (write_retry(out, outbufp, BUFLEN) != BUFLEN) {
660166255Sdelphij				maybe_warn("write");
661166255Sdelphij				out_tot = -1;
662166255Sdelphij				goto out;
663166255Sdelphij			}
664166255Sdelphij
665166255Sdelphij			out_tot += BUFLEN;
666166255Sdelphij			z.next_out = (unsigned char *)outbufp;
667166255Sdelphij			z.avail_out = BUFLEN;
668166255Sdelphij		}
669166255Sdelphij
670166255Sdelphij		if (z.avail_in == 0) {
671166255Sdelphij			in_size = read(in, inbufp, BUFLEN);
672166255Sdelphij			if (in_size < 0) {
673166255Sdelphij				maybe_warn("read");
674166255Sdelphij				in_tot = -1;
675166255Sdelphij				goto out;
676166255Sdelphij			}
677166255Sdelphij			if (in_size == 0)
678166255Sdelphij				break;
679326559Sdelphij			infile_newdata(in_size);
680166255Sdelphij
681166255Sdelphij			crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
682166255Sdelphij			in_tot += in_size;
683166255Sdelphij			z.next_in = (unsigned char *)inbufp;
684166255Sdelphij			z.avail_in = in_size;
685166255Sdelphij		}
686166255Sdelphij
687166255Sdelphij		error = deflate(&z, Z_NO_FLUSH);
688166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
689166255Sdelphij			maybe_warnx("deflate failed");
690166255Sdelphij			in_tot = -1;
691166255Sdelphij			goto out;
692166255Sdelphij		}
693166255Sdelphij	}
694166255Sdelphij
695166255Sdelphij	/* clean up */
696166255Sdelphij	for (;;) {
697166255Sdelphij		size_t len;
698166255Sdelphij		ssize_t w;
699166255Sdelphij
700166255Sdelphij		error = deflate(&z, Z_FINISH);
701166255Sdelphij		if (error != Z_OK && error != Z_STREAM_END) {
702166255Sdelphij			maybe_warnx("deflate failed");
703166255Sdelphij			in_tot = -1;
704166255Sdelphij			goto out;
705166255Sdelphij		}
706166255Sdelphij
707166255Sdelphij		len = (char *)z.next_out - outbufp;
708166255Sdelphij
709326559Sdelphij		w = write_retry(out, outbufp, len);
710166255Sdelphij		if (w == -1 || (size_t)w != len) {
711166255Sdelphij			maybe_warn("write");
712166255Sdelphij			out_tot = -1;
713166255Sdelphij			goto out;
714166255Sdelphij		}
715166255Sdelphij		out_tot += len;
716166255Sdelphij		z.next_out = (unsigned char *)outbufp;
717166255Sdelphij		z.avail_out = BUFLEN;
718166255Sdelphij
719166255Sdelphij		if (error == Z_STREAM_END)
720166255Sdelphij			break;
721166255Sdelphij	}
722166255Sdelphij
723166255Sdelphij	if (deflateEnd(&z) != Z_OK) {
724166255Sdelphij		maybe_warnx("deflateEnd failed");
725166255Sdelphij		in_tot = -1;
726166255Sdelphij		goto out;
727166255Sdelphij	}
728166255Sdelphij
729326559Sdelphij	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
730166255Sdelphij		 (int)crc & 0xff,
731166255Sdelphij		 (int)(crc >> 8) & 0xff,
732166255Sdelphij		 (int)(crc >> 16) & 0xff,
733166255Sdelphij		 (int)(crc >> 24) & 0xff,
734166255Sdelphij		 (int)in_tot & 0xff,
735166255Sdelphij		 (int)(in_tot >> 8) & 0xff,
736166255Sdelphij		 (int)(in_tot >> 16) & 0xff,
737166255Sdelphij		 (int)(in_tot >> 24) & 0xff);
738166255Sdelphij	if (i != 8)
739166255Sdelphij		maybe_err("snprintf");
740326559Sdelphij	if (write_retry(out, outbufp, i) != i) {
741166255Sdelphij		maybe_warn("write");
742166255Sdelphij		in_tot = -1;
743166255Sdelphij	} else
744166255Sdelphij		out_tot += i;
745166255Sdelphij
746166255Sdelphijout:
747166255Sdelphij	if (inbufp != NULL)
748166255Sdelphij		free(inbufp);
749166255Sdelphij	if (outbufp != NULL)
750166255Sdelphij		free(outbufp);
751166255Sdelphij	if (gsizep)
752166255Sdelphij		*gsizep = out_tot;
753166255Sdelphij	return in_tot;
754166255Sdelphij}
755166255Sdelphij
756166255Sdelphij/*
757166255Sdelphij * uncompress input to output then close the input.  return the
758166255Sdelphij * uncompressed size written, and put the compressed sized read
759166255Sdelphij * into `*gsizep'.
760166255Sdelphij */
761166255Sdelphijstatic off_t
762166255Sdelphijgz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
763166255Sdelphij	      const char *filename)
764166255Sdelphij{
765166255Sdelphij	z_stream z;
766166255Sdelphij	char *outbufp, *inbufp;
767166255Sdelphij	off_t out_tot = -1, in_tot = 0;
768166255Sdelphij	uint32_t out_sub_tot = 0;
769166255Sdelphij	enum {
770166255Sdelphij		GZSTATE_MAGIC0,
771166255Sdelphij		GZSTATE_MAGIC1,
772166255Sdelphij		GZSTATE_METHOD,
773166255Sdelphij		GZSTATE_FLAGS,
774166255Sdelphij		GZSTATE_SKIPPING,
775166255Sdelphij		GZSTATE_EXTRA,
776166255Sdelphij		GZSTATE_EXTRA2,
777166255Sdelphij		GZSTATE_EXTRA3,
778166255Sdelphij		GZSTATE_ORIGNAME,
779166255Sdelphij		GZSTATE_COMMENT,
780166255Sdelphij		GZSTATE_HEAD_CRC1,
781166255Sdelphij		GZSTATE_HEAD_CRC2,
782166255Sdelphij		GZSTATE_INIT,
783166255Sdelphij		GZSTATE_READ,
784166255Sdelphij		GZSTATE_CRC,
785166255Sdelphij		GZSTATE_LEN,
786166255Sdelphij	} state = GZSTATE_MAGIC0;
787166255Sdelphij	int flags = 0, skip_count = 0;
788166255Sdelphij	int error = Z_STREAM_ERROR, done_reading = 0;
789166255Sdelphij	uLong crc = 0;
790166255Sdelphij	ssize_t wr;
791166255Sdelphij	int needmore = 0;
792166255Sdelphij
793166255Sdelphij#define ADVANCE()       { z.next_in++; z.avail_in--; }
794166255Sdelphij
795166255Sdelphij	if ((outbufp = malloc(BUFLEN)) == NULL) {
796166255Sdelphij		maybe_err("malloc failed");
797166255Sdelphij		goto out2;
798166255Sdelphij	}
799166255Sdelphij	if ((inbufp = malloc(BUFLEN)) == NULL) {
800166255Sdelphij		maybe_err("malloc failed");
801166255Sdelphij		goto out1;
802166255Sdelphij	}
803166255Sdelphij
804166255Sdelphij	memset(&z, 0, sizeof z);
805166255Sdelphij	z.avail_in = prelen;
806166255Sdelphij	z.next_in = (unsigned char *)pre;
807166255Sdelphij	z.avail_out = BUFLEN;
808166255Sdelphij	z.next_out = (unsigned char *)outbufp;
809166255Sdelphij	z.zalloc = NULL;
810166255Sdelphij	z.zfree = NULL;
811166255Sdelphij	z.opaque = 0;
812166255Sdelphij
813166255Sdelphij	in_tot = prelen;
814166255Sdelphij	out_tot = 0;
815166255Sdelphij
816166255Sdelphij	for (;;) {
817326559Sdelphij		check_siginfo();
818166255Sdelphij		if ((z.avail_in == 0 || needmore) && done_reading == 0) {
819166255Sdelphij			ssize_t in_size;
820166255Sdelphij
821166255Sdelphij			if (z.avail_in > 0) {
822166255Sdelphij				memmove(inbufp, z.next_in, z.avail_in);
823166255Sdelphij			}
824166255Sdelphij			z.next_in = (unsigned char *)inbufp;
825166255Sdelphij			in_size = read(in, z.next_in + z.avail_in,
826166255Sdelphij			    BUFLEN - z.avail_in);
827166255Sdelphij
828166255Sdelphij			if (in_size == -1) {
829166255Sdelphij				maybe_warn("failed to read stdin");
830166255Sdelphij				goto stop_and_fail;
831166255Sdelphij			} else if (in_size == 0) {
832166255Sdelphij				done_reading = 1;
833166255Sdelphij			}
834326559Sdelphij			infile_newdata(in_size);
835166255Sdelphij
836166255Sdelphij			z.avail_in += in_size;
837166255Sdelphij			needmore = 0;
838166255Sdelphij
839166255Sdelphij			in_tot += in_size;
840166255Sdelphij		}
841166255Sdelphij		if (z.avail_in == 0) {
842166255Sdelphij			if (done_reading && state != GZSTATE_MAGIC0) {
843166255Sdelphij				maybe_warnx("%s: unexpected end of file",
844166255Sdelphij					    filename);
845166255Sdelphij				goto stop_and_fail;
846166255Sdelphij			}
847166255Sdelphij			goto stop;
848166255Sdelphij		}
849166255Sdelphij		switch (state) {
850166255Sdelphij		case GZSTATE_MAGIC0:
851166255Sdelphij			if (*z.next_in != GZIP_MAGIC0) {
852166255Sdelphij				if (in_tot > 0) {
853166255Sdelphij					maybe_warnx("%s: trailing garbage "
854166255Sdelphij						    "ignored", filename);
855290024Sdelphij					exit_value = 2;
856166255Sdelphij					goto stop;
857166255Sdelphij				}
858166255Sdelphij				maybe_warnx("input not gziped (MAGIC0)");
859166255Sdelphij				goto stop_and_fail;
860166255Sdelphij			}
861166255Sdelphij			ADVANCE();
862166255Sdelphij			state++;
863166255Sdelphij			out_sub_tot = 0;
864166255Sdelphij			crc = crc32(0L, Z_NULL, 0);
865166255Sdelphij			break;
866166255Sdelphij
867166255Sdelphij		case GZSTATE_MAGIC1:
868166255Sdelphij			if (*z.next_in != GZIP_MAGIC1 &&
869166255Sdelphij			    *z.next_in != GZIP_OMAGIC1) {
870166255Sdelphij				maybe_warnx("input not gziped (MAGIC1)");
871166255Sdelphij				goto stop_and_fail;
872166255Sdelphij			}
873166255Sdelphij			ADVANCE();
874166255Sdelphij			state++;
875166255Sdelphij			break;
876166255Sdelphij
877166255Sdelphij		case GZSTATE_METHOD:
878166255Sdelphij			if (*z.next_in != Z_DEFLATED) {
879166255Sdelphij				maybe_warnx("unknown compression method");
880166255Sdelphij				goto stop_and_fail;
881166255Sdelphij			}
882166255Sdelphij			ADVANCE();
883166255Sdelphij			state++;
884166255Sdelphij			break;
885166255Sdelphij
886166255Sdelphij		case GZSTATE_FLAGS:
887166255Sdelphij			flags = *z.next_in;
888166255Sdelphij			ADVANCE();
889166255Sdelphij			skip_count = 6;
890166255Sdelphij			state++;
891166255Sdelphij			break;
892166255Sdelphij
893166255Sdelphij		case GZSTATE_SKIPPING:
894166255Sdelphij			if (skip_count > 0) {
895166255Sdelphij				skip_count--;
896166255Sdelphij				ADVANCE();
897166255Sdelphij			} else
898166255Sdelphij				state++;
899166255Sdelphij			break;
900166255Sdelphij
901166255Sdelphij		case GZSTATE_EXTRA:
902166255Sdelphij			if ((flags & EXTRA_FIELD) == 0) {
903166255Sdelphij				state = GZSTATE_ORIGNAME;
904166255Sdelphij				break;
905166255Sdelphij			}
906166255Sdelphij			skip_count = *z.next_in;
907166255Sdelphij			ADVANCE();
908166255Sdelphij			state++;
909166255Sdelphij			break;
910166255Sdelphij
911166255Sdelphij		case GZSTATE_EXTRA2:
912166255Sdelphij			skip_count |= ((*z.next_in) << 8);
913166255Sdelphij			ADVANCE();
914166255Sdelphij			state++;
915166255Sdelphij			break;
916166255Sdelphij
917166255Sdelphij		case GZSTATE_EXTRA3:
918166255Sdelphij			if (skip_count > 0) {
919166255Sdelphij				skip_count--;
920166255Sdelphij				ADVANCE();
921166255Sdelphij			} else
922166255Sdelphij				state++;
923166255Sdelphij			break;
924166255Sdelphij
925166255Sdelphij		case GZSTATE_ORIGNAME:
926166255Sdelphij			if ((flags & ORIG_NAME) == 0) {
927166255Sdelphij				state++;
928166255Sdelphij				break;
929166255Sdelphij			}
930166255Sdelphij			if (*z.next_in == 0)
931166255Sdelphij				state++;
932166255Sdelphij			ADVANCE();
933166255Sdelphij			break;
934166255Sdelphij
935166255Sdelphij		case GZSTATE_COMMENT:
936166255Sdelphij			if ((flags & COMMENT) == 0) {
937166255Sdelphij				state++;
938166255Sdelphij				break;
939166255Sdelphij			}
940166255Sdelphij			if (*z.next_in == 0)
941166255Sdelphij				state++;
942166255Sdelphij			ADVANCE();
943166255Sdelphij			break;
944166255Sdelphij
945166255Sdelphij		case GZSTATE_HEAD_CRC1:
946166255Sdelphij			if (flags & HEAD_CRC)
947166255Sdelphij				skip_count = 2;
948166255Sdelphij			else
949166255Sdelphij				skip_count = 0;
950166255Sdelphij			state++;
951166255Sdelphij			break;
952166255Sdelphij
953166255Sdelphij		case GZSTATE_HEAD_CRC2:
954166255Sdelphij			if (skip_count > 0) {
955166255Sdelphij				skip_count--;
956166255Sdelphij				ADVANCE();
957166255Sdelphij			} else
958166255Sdelphij				state++;
959166255Sdelphij			break;
960166255Sdelphij
961166255Sdelphij		case GZSTATE_INIT:
962166255Sdelphij			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
963166255Sdelphij				maybe_warnx("failed to inflateInit");
964166255Sdelphij				goto stop_and_fail;
965166255Sdelphij			}
966166255Sdelphij			state++;
967166255Sdelphij			break;
968166255Sdelphij
969166255Sdelphij		case GZSTATE_READ:
970166255Sdelphij			error = inflate(&z, Z_FINISH);
971166255Sdelphij			switch (error) {
972166255Sdelphij			/* Z_BUF_ERROR goes with Z_FINISH... */
973166255Sdelphij			case Z_BUF_ERROR:
974213044Sdelphij				if (z.avail_out > 0 && !done_reading)
975213044Sdelphij					continue;
976222210Sdelphij
977166255Sdelphij			case Z_STREAM_END:
978166255Sdelphij			case Z_OK:
979166255Sdelphij				break;
980166255Sdelphij
981166255Sdelphij			case Z_NEED_DICT:
982166255Sdelphij				maybe_warnx("Z_NEED_DICT error");
983166255Sdelphij				goto stop_and_fail;
984166255Sdelphij			case Z_DATA_ERROR:
985166255Sdelphij				maybe_warnx("data stream error");
986166255Sdelphij				goto stop_and_fail;
987166255Sdelphij			case Z_STREAM_ERROR:
988166255Sdelphij				maybe_warnx("internal stream error");
989166255Sdelphij				goto stop_and_fail;
990166255Sdelphij			case Z_MEM_ERROR:
991166255Sdelphij				maybe_warnx("memory allocation error");
992166255Sdelphij				goto stop_and_fail;
993166255Sdelphij
994166255Sdelphij			default:
995166255Sdelphij				maybe_warn("unknown error from inflate(): %d",
996166255Sdelphij				    error);
997166255Sdelphij			}
998166255Sdelphij			wr = BUFLEN - z.avail_out;
999166255Sdelphij
1000166255Sdelphij			if (wr != 0) {
1001166255Sdelphij				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
1002166255Sdelphij				if (
1003166255Sdelphij#ifndef SMALL
1004166255Sdelphij				    /* don't write anything with -t */
1005166255Sdelphij				    tflag == 0 &&
1006166255Sdelphij#endif
1007326559Sdelphij				    write_retry(out, outbufp, wr) != wr) {
1008166255Sdelphij					maybe_warn("error writing to output");
1009166255Sdelphij					goto stop_and_fail;
1010166255Sdelphij				}
1011166255Sdelphij
1012166255Sdelphij				out_tot += wr;
1013166255Sdelphij				out_sub_tot += wr;
1014166255Sdelphij			}
1015166255Sdelphij
1016166255Sdelphij			if (error == Z_STREAM_END) {
1017166255Sdelphij				inflateEnd(&z);
1018166255Sdelphij				state++;
1019166255Sdelphij			}
1020166255Sdelphij
1021166255Sdelphij			z.next_out = (unsigned char *)outbufp;
1022166255Sdelphij			z.avail_out = BUFLEN;
1023166255Sdelphij
1024166255Sdelphij			break;
1025166255Sdelphij		case GZSTATE_CRC:
1026166255Sdelphij			{
1027166255Sdelphij				uLong origcrc;
1028166255Sdelphij
1029166255Sdelphij				if (z.avail_in < 4) {
1030166255Sdelphij					if (!done_reading) {
1031166255Sdelphij						needmore = 1;
1032166255Sdelphij						continue;
1033166255Sdelphij					}
1034166255Sdelphij					maybe_warnx("truncated input");
1035166255Sdelphij					goto stop_and_fail;
1036166255Sdelphij				}
1037336661Sdelphij				origcrc = le32dec(&z.next_in[0]);
1038166255Sdelphij				if (origcrc != crc) {
1039166255Sdelphij					maybe_warnx("invalid compressed"
1040166255Sdelphij					     " data--crc error");
1041166255Sdelphij					goto stop_and_fail;
1042166255Sdelphij				}
1043166255Sdelphij			}
1044166255Sdelphij
1045166255Sdelphij			z.avail_in -= 4;
1046166255Sdelphij			z.next_in += 4;
1047166255Sdelphij
1048166255Sdelphij			if (!z.avail_in && done_reading) {
1049166255Sdelphij				goto stop;
1050166255Sdelphij			}
1051166255Sdelphij			state++;
1052166255Sdelphij			break;
1053166255Sdelphij		case GZSTATE_LEN:
1054166255Sdelphij			{
1055166255Sdelphij				uLong origlen;
1056166255Sdelphij
1057166255Sdelphij				if (z.avail_in < 4) {
1058166255Sdelphij					if (!done_reading) {
1059166255Sdelphij						needmore = 1;
1060166255Sdelphij						continue;
1061166255Sdelphij					}
1062166255Sdelphij					maybe_warnx("truncated input");
1063166255Sdelphij					goto stop_and_fail;
1064166255Sdelphij				}
1065336661Sdelphij				origlen = le32dec(&z.next_in[0]);
1066166255Sdelphij
1067166255Sdelphij				if (origlen != out_sub_tot) {
1068166255Sdelphij					maybe_warnx("invalid compressed"
1069166255Sdelphij					     " data--length error");
1070166255Sdelphij					goto stop_and_fail;
1071166255Sdelphij				}
1072166255Sdelphij			}
1073166255Sdelphij
1074166255Sdelphij			z.avail_in -= 4;
1075166255Sdelphij			z.next_in += 4;
1076166255Sdelphij
1077166255Sdelphij			if (error < 0) {
1078166255Sdelphij				maybe_warnx("decompression error");
1079166255Sdelphij				goto stop_and_fail;
1080166255Sdelphij			}
1081166255Sdelphij			state = GZSTATE_MAGIC0;
1082166255Sdelphij			break;
1083166255Sdelphij		}
1084166255Sdelphij		continue;
1085166255Sdelphijstop_and_fail:
1086166255Sdelphij		out_tot = -1;
1087166255Sdelphijstop:
1088166255Sdelphij		break;
1089166255Sdelphij	}
1090166255Sdelphij	if (state > GZSTATE_INIT)
1091166255Sdelphij		inflateEnd(&z);
1092166255Sdelphij
1093166255Sdelphij	free(inbufp);
1094166255Sdelphijout1:
1095166255Sdelphij	free(outbufp);
1096166255Sdelphijout2:
1097166255Sdelphij	if (gsizep)
1098166255Sdelphij		*gsizep = in_tot;
1099166255Sdelphij	return (out_tot);
1100166255Sdelphij}
1101166255Sdelphij
1102166255Sdelphij#ifndef SMALL
1103166255Sdelphij/*
1104166255Sdelphij * set the owner, mode, flags & utimes using the given file descriptor.
1105166255Sdelphij * file is only used in possible warning messages.
1106166255Sdelphij */
1107166255Sdelphijstatic void
1108166255Sdelphijcopymodes(int fd, const struct stat *sbp, const char *file)
1109166255Sdelphij{
1110278896Sjilles	struct timespec times[2];
1111166255Sdelphij	struct stat sb;
1112166255Sdelphij
1113166255Sdelphij	/*
1114166255Sdelphij	 * If we have no info on the input, give this file some
1115166255Sdelphij	 * default values and return..
1116166255Sdelphij	 */
1117166255Sdelphij	if (sbp == NULL) {
1118166255Sdelphij		mode_t mask = umask(022);
1119166255Sdelphij
1120166255Sdelphij		(void)fchmod(fd, DEFFILEMODE & ~mask);
1121166255Sdelphij		(void)umask(mask);
1122326559Sdelphij		return;
1123166255Sdelphij	}
1124166255Sdelphij	sb = *sbp;
1125166255Sdelphij
1126166255Sdelphij	/* if the chown fails, remove set-id bits as-per compress(1) */
1127166255Sdelphij	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1128166255Sdelphij		if (errno != EPERM)
1129166255Sdelphij			maybe_warn("couldn't fchown: %s", file);
1130166255Sdelphij		sb.st_mode &= ~(S_ISUID|S_ISGID);
1131166255Sdelphij	}
1132166255Sdelphij
1133166255Sdelphij	/* we only allow set-id and the 9 normal permission bits */
1134166255Sdelphij	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1135166255Sdelphij	if (fchmod(fd, sb.st_mode) < 0)
1136166255Sdelphij		maybe_warn("couldn't fchmod: %s", file);
1137166255Sdelphij
1138278896Sjilles	times[0] = sb.st_atim;
1139278896Sjilles	times[1] = sb.st_mtim;
1140278896Sjilles	if (futimens(fd, times) < 0)
1141278896Sjilles		maybe_warn("couldn't futimens: %s", file);
1142176970Srwatson
1143176970Srwatson	/* only try flags if they exist already */
1144176970Srwatson        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1145176970Srwatson		maybe_warn("couldn't fchflags: %s", file);
1146166255Sdelphij}
1147166255Sdelphij#endif
1148166255Sdelphij
1149166255Sdelphij/* what sort of file is this? */
1150166255Sdelphijstatic enum filetype
1151166255Sdelphijfile_gettype(u_char *buf)
1152166255Sdelphij{
1153166255Sdelphij
1154166255Sdelphij	if (buf[0] == GZIP_MAGIC0 &&
1155166255Sdelphij	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1156166255Sdelphij		return FT_GZIP;
1157166255Sdelphij	else
1158166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1159166255Sdelphij	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1160166255Sdelphij	    buf[3] >= '0' && buf[3] <= '9')
1161166255Sdelphij		return FT_BZIP2;
1162166255Sdelphij	else
1163166255Sdelphij#endif
1164166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1165166255Sdelphij	if (memcmp(buf, Z_MAGIC, 2) == 0)
1166166255Sdelphij		return FT_Z;
1167166255Sdelphij	else
1168166255Sdelphij#endif
1169194579Sdelphij#ifndef NO_PACK_SUPPORT
1170194579Sdelphij	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1171194579Sdelphij		return FT_PACK;
1172194579Sdelphij	else
1173194579Sdelphij#endif
1174226184Sdelphij#ifndef NO_XZ_SUPPORT
1175226184Sdelphij	if (memcmp(buf, XZ_MAGIC, 4) == 0)	/* XXX: We only have 4 bytes */
1176226184Sdelphij		return FT_XZ;
1177226184Sdelphij	else
1178226184Sdelphij#endif
1179343251Sdelphij#ifndef NO_LZ_SUPPORT
1180343251Sdelphij	if (memcmp(buf, LZ_MAGIC, 4) == 0)
1181343251Sdelphij		return FT_LZ;
1182343251Sdelphij	else
1183343251Sdelphij#endif
1184166255Sdelphij		return FT_UNKNOWN;
1185166255Sdelphij}
1186166255Sdelphij
1187166255Sdelphij#ifndef SMALL
1188166255Sdelphij/* check the outfile is OK. */
1189166255Sdelphijstatic int
1190166255Sdelphijcheck_outfile(const char *outfile)
1191166255Sdelphij{
1192166255Sdelphij	struct stat sb;
1193166255Sdelphij	int ok = 1;
1194166255Sdelphij
1195166255Sdelphij	if (lflag == 0 && stat(outfile, &sb) == 0) {
1196166255Sdelphij		if (fflag)
1197166255Sdelphij			unlink(outfile);
1198166255Sdelphij		else if (isatty(STDIN_FILENO)) {
1199166255Sdelphij			char ans[10] = { 'n', '\0' };	/* default */
1200166255Sdelphij
1201166255Sdelphij			fprintf(stderr, "%s already exists -- do you wish to "
1202166255Sdelphij					"overwrite (y or n)? " , outfile);
1203166255Sdelphij			(void)fgets(ans, sizeof(ans) - 1, stdin);
1204166255Sdelphij			if (ans[0] != 'y' && ans[0] != 'Y') {
1205166363Sdelphij				fprintf(stderr, "\tnot overwriting\n");
1206166255Sdelphij				ok = 0;
1207166255Sdelphij			} else
1208166255Sdelphij				unlink(outfile);
1209166255Sdelphij		} else {
1210166255Sdelphij			maybe_warnx("%s already exists -- skipping", outfile);
1211166255Sdelphij			ok = 0;
1212166255Sdelphij		}
1213166255Sdelphij	}
1214166255Sdelphij	return ok;
1215166255Sdelphij}
1216166255Sdelphij
1217166255Sdelphijstatic void
1218166255Sdelphijunlink_input(const char *file, const struct stat *sb)
1219166255Sdelphij{
1220166255Sdelphij	struct stat nsb;
1221166255Sdelphij
1222170053Sdelphij	if (kflag)
1223170053Sdelphij		return;
1224166255Sdelphij	if (stat(file, &nsb) != 0)
1225213927Sbcr		/* Must be gone already */
1226166255Sdelphij		return;
1227166255Sdelphij	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1228166255Sdelphij		/* Definitely a different file */
1229166255Sdelphij		return;
1230166255Sdelphij	unlink(file);
1231166255Sdelphij}
1232207247Sdelphij
1233207247Sdelphijstatic void
1234326559Sdelphijgot_sigint(int signo __unused)
1235207247Sdelphij{
1236207247Sdelphij
1237207247Sdelphij	if (remove_file != NULL)
1238207247Sdelphij		unlink(remove_file);
1239207284Sdelphij	_exit(2);
1240207247Sdelphij}
1241326559Sdelphij
1242326559Sdelphijstatic void
1243326559Sdelphijgot_siginfo(int signo __unused)
1244326559Sdelphij{
1245326559Sdelphij
1246326559Sdelphij	print_info = 1;
1247326559Sdelphij}
1248326559Sdelphij
1249326559Sdelphijstatic void
1250326559Sdelphijsetup_signals(void)
1251326559Sdelphij{
1252326559Sdelphij
1253326559Sdelphij	signal(SIGINFO, got_siginfo);
1254326559Sdelphij	signal(SIGINT, got_sigint);
1255326559Sdelphij}
1256326559Sdelphij
1257326559Sdelphijstatic	void
1258326559Sdelphijinfile_newdata(size_t newdata)
1259326559Sdelphij{
1260326559Sdelphij
1261326559Sdelphij	infile_current += newdata;
1262326559Sdelphij}
1263166255Sdelphij#endif
1264166255Sdelphij
1265326559Sdelphijstatic	void
1266326559Sdelphijinfile_set(const char *newinfile, off_t total)
1267326559Sdelphij{
1268326559Sdelphij
1269326559Sdelphij	if (newinfile)
1270326559Sdelphij		infile = newinfile;
1271326559Sdelphij#ifndef SMALL
1272326559Sdelphij	infile_total = total;
1273326559Sdelphij#endif
1274326559Sdelphij}
1275326559Sdelphij
1276326559Sdelphijstatic	void
1277326559Sdelphijinfile_clear(void)
1278326559Sdelphij{
1279326559Sdelphij
1280326559Sdelphij	infile = NULL;
1281326559Sdelphij#ifndef SMALL
1282326559Sdelphij	infile_total = infile_current = 0;
1283326559Sdelphij#endif
1284326559Sdelphij}
1285326559Sdelphij
1286166255Sdelphijstatic const suffixes_t *
1287166255Sdelphijcheck_suffix(char *file, int xlate)
1288166255Sdelphij{
1289166255Sdelphij	const suffixes_t *s;
1290166255Sdelphij	int len = strlen(file);
1291166255Sdelphij	char *sp;
1292166255Sdelphij
1293166255Sdelphij	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1294166255Sdelphij		/* if it doesn't fit in "a.suf", don't bother */
1295166255Sdelphij		if (s->ziplen >= len)
1296166255Sdelphij			continue;
1297166255Sdelphij		sp = file + len - s->ziplen;
1298166255Sdelphij		if (strcmp(s->zipped, sp) != 0)
1299166255Sdelphij			continue;
1300166255Sdelphij		if (xlate)
1301166255Sdelphij			strcpy(sp, s->normal);
1302166255Sdelphij		return s;
1303166255Sdelphij	}
1304166255Sdelphij	return NULL;
1305166255Sdelphij}
1306166255Sdelphij
1307166255Sdelphij/*
1308166255Sdelphij * compress the given file: create a corresponding .gz file and remove the
1309166255Sdelphij * original.
1310166255Sdelphij */
1311166255Sdelphijstatic off_t
1312166255Sdelphijfile_compress(char *file, char *outfile, size_t outsize)
1313166255Sdelphij{
1314166255Sdelphij	int in;
1315166255Sdelphij	int out;
1316326559Sdelphij	off_t size, in_size;
1317166255Sdelphij#ifndef SMALL
1318166255Sdelphij	struct stat isb, osb;
1319166255Sdelphij	const suffixes_t *suff;
1320166255Sdelphij#endif
1321166255Sdelphij
1322166255Sdelphij	in = open(file, O_RDONLY);
1323166255Sdelphij	if (in == -1) {
1324166255Sdelphij		maybe_warn("can't open %s", file);
1325209017Sdelphij		return (-1);
1326166255Sdelphij	}
1327166255Sdelphij
1328208888Sdelphij#ifndef SMALL
1329208888Sdelphij	if (fstat(in, &isb) != 0) {
1330208888Sdelphij		maybe_warn("couldn't stat: %s", file);
1331208888Sdelphij		close(in);
1332209017Sdelphij		return (-1);
1333208888Sdelphij	}
1334208888Sdelphij#endif
1335208888Sdelphij
1336326559Sdelphij#ifndef SMALL
1337326559Sdelphij	if (fstat(in, &isb) != 0) {
1338326559Sdelphij		close(in);
1339326559Sdelphij		maybe_warn("can't stat %s", file);
1340326559Sdelphij		return -1;
1341326559Sdelphij	}
1342326559Sdelphij	infile_set(file, isb.st_size);
1343326559Sdelphij#endif
1344326559Sdelphij
1345166255Sdelphij	if (cflag == 0) {
1346166255Sdelphij#ifndef SMALL
1347209017Sdelphij		if (isb.st_nlink > 1 && fflag == 0) {
1348326559Sdelphij			maybe_warnx("%s has %ju other link%s -- "
1349326559Sdelphij				    "skipping", file,
1350326559Sdelphij				    (uintmax_t)isb.st_nlink - 1,
1351326559Sdelphij				    isb.st_nlink == 1 ? "" : "s");
1352208889Sdelphij			close(in);
1353326559Sdelphij			return -1;
1354208889Sdelphij		}
1355166255Sdelphij
1356209017Sdelphij		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1357209017Sdelphij		    suff->zipped[0] != 0) {
1358166255Sdelphij			maybe_warnx("%s already has %s suffix -- unchanged",
1359209017Sdelphij			    file, suff->zipped);
1360166255Sdelphij			close(in);
1361209017Sdelphij			return (-1);
1362166255Sdelphij		}
1363166255Sdelphij#endif
1364166255Sdelphij
1365166255Sdelphij		/* Add (usually) .gz to filename */
1366166255Sdelphij		if ((size_t)snprintf(outfile, outsize, "%s%s",
1367209017Sdelphij		    file, suffixes[0].zipped) >= outsize)
1368195988Sdelphij			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1369209017Sdelphij			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1370166255Sdelphij
1371166255Sdelphij#ifndef SMALL
1372166255Sdelphij		if (check_outfile(outfile) == 0) {
1373166255Sdelphij			close(in);
1374209017Sdelphij			return (-1);
1375166255Sdelphij		}
1376166255Sdelphij#endif
1377166255Sdelphij	}
1378166255Sdelphij
1379166255Sdelphij	if (cflag == 0) {
1380166255Sdelphij		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1381166255Sdelphij		if (out == -1) {
1382166255Sdelphij			maybe_warn("could not create output: %s", outfile);
1383166255Sdelphij			fclose(stdin);
1384209017Sdelphij			return (-1);
1385166255Sdelphij		}
1386207247Sdelphij#ifndef SMALL
1387207247Sdelphij		remove_file = outfile;
1388207247Sdelphij#endif
1389166255Sdelphij	} else
1390166255Sdelphij		out = STDOUT_FILENO;
1391166255Sdelphij
1392326559Sdelphij	in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1393166255Sdelphij
1394166255Sdelphij	(void)close(in);
1395166255Sdelphij
1396166255Sdelphij	/*
1397326559Sdelphij	 * If there was an error, in_size will be -1.
1398166255Sdelphij	 * If we compressed to stdout, just return the size.
1399166255Sdelphij	 * Otherwise stat the file and check it is the correct size.
1400166255Sdelphij	 * We only blow away the file if we can stat the output and it
1401166255Sdelphij	 * has the expected size.
1402166255Sdelphij	 */
1403166255Sdelphij	if (cflag != 0)
1404326559Sdelphij		return in_size == -1 ? -1 : size;
1405166255Sdelphij
1406166255Sdelphij#ifndef SMALL
1407166255Sdelphij	if (fstat(out, &osb) != 0) {
1408166255Sdelphij		maybe_warn("couldn't stat: %s", outfile);
1409166255Sdelphij		goto bad_outfile;
1410166255Sdelphij	}
1411166255Sdelphij
1412166255Sdelphij	if (osb.st_size != size) {
1413209017Sdelphij		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1414209017Sdelphij		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1415166255Sdelphij		goto bad_outfile;
1416166255Sdelphij	}
1417166255Sdelphij
1418166255Sdelphij	copymodes(out, &isb, outfile);
1419207247Sdelphij	remove_file = NULL;
1420166255Sdelphij#endif
1421166255Sdelphij	if (close(out) == -1)
1422166255Sdelphij		maybe_warn("couldn't close output");
1423166255Sdelphij
1424166255Sdelphij	/* output is good, ok to delete input */
1425166255Sdelphij	unlink_input(file, &isb);
1426209017Sdelphij	return (size);
1427166255Sdelphij
1428166255Sdelphij#ifndef SMALL
1429166255Sdelphij    bad_outfile:
1430166255Sdelphij	if (close(out) == -1)
1431166255Sdelphij		maybe_warn("couldn't close output");
1432166255Sdelphij
1433166255Sdelphij	maybe_warnx("leaving original %s", file);
1434166255Sdelphij	unlink(outfile);
1435209017Sdelphij	return (size);
1436166255Sdelphij#endif
1437166255Sdelphij}
1438166255Sdelphij
1439166255Sdelphij/* uncompress the given file and remove the original */
1440166255Sdelphijstatic off_t
1441166255Sdelphijfile_uncompress(char *file, char *outfile, size_t outsize)
1442166255Sdelphij{
1443166255Sdelphij	struct stat isb, osb;
1444166255Sdelphij	off_t size;
1445166255Sdelphij	ssize_t rbytes;
1446166255Sdelphij	unsigned char header1[4];
1447166255Sdelphij	enum filetype method;
1448194508Sdelphij	int fd, ofd, zfd = -1;
1449337521Sdelphij	int error;
1450326559Sdelphij	size_t in_size;
1451166255Sdelphij#ifndef SMALL
1452206387Sdelphij	ssize_t rv;
1453166255Sdelphij	time_t timestamp = 0;
1454281500Sdelphij	char name[PATH_MAX + 1];
1455166255Sdelphij#endif
1456166255Sdelphij
1457166255Sdelphij	/* gather the old name info */
1458166255Sdelphij
1459166255Sdelphij	fd = open(file, O_RDONLY);
1460166255Sdelphij	if (fd < 0) {
1461166255Sdelphij		maybe_warn("can't open %s", file);
1462166255Sdelphij		goto lose;
1463166255Sdelphij	}
1464326559Sdelphij	if (fstat(fd, &isb) != 0) {
1465326559Sdelphij		close(fd);
1466326559Sdelphij		maybe_warn("can't stat %s", file);
1467326559Sdelphij		goto lose;
1468326559Sdelphij	}
1469326559Sdelphij	if (S_ISREG(isb.st_mode))
1470326559Sdelphij		in_size = isb.st_size;
1471326559Sdelphij	else
1472326559Sdelphij		in_size = 0;
1473326559Sdelphij	infile_set(file, in_size);
1474166255Sdelphij
1475166255Sdelphij	strlcpy(outfile, file, outsize);
1476166255Sdelphij	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1477166255Sdelphij		maybe_warnx("%s: unknown suffix -- ignored", file);
1478166255Sdelphij		goto lose;
1479166255Sdelphij	}
1480166255Sdelphij
1481166255Sdelphij	rbytes = read(fd, header1, sizeof header1);
1482166255Sdelphij	if (rbytes != sizeof header1) {
1483166255Sdelphij		/* we don't want to fail here. */
1484166255Sdelphij#ifndef SMALL
1485166255Sdelphij		if (fflag)
1486166255Sdelphij			goto lose;
1487166255Sdelphij#endif
1488166255Sdelphij		if (rbytes == -1)
1489166255Sdelphij			maybe_warn("can't read %s", file);
1490166255Sdelphij		else
1491166255Sdelphij			goto unexpected_EOF;
1492166255Sdelphij		goto lose;
1493166255Sdelphij	}
1494326559Sdelphij	infile_newdata(rbytes);
1495166255Sdelphij
1496166255Sdelphij	method = file_gettype(header1);
1497166255Sdelphij#ifndef SMALL
1498166255Sdelphij	if (fflag == 0 && method == FT_UNKNOWN) {
1499166255Sdelphij		maybe_warnx("%s: not in gzip format", file);
1500166255Sdelphij		goto lose;
1501166255Sdelphij	}
1502166255Sdelphij
1503166255Sdelphij#endif
1504166255Sdelphij
1505166255Sdelphij#ifndef SMALL
1506166255Sdelphij	if (method == FT_GZIP && Nflag) {
1507166255Sdelphij		unsigned char ts[4];	/* timestamp */
1508166255Sdelphij
1509166255Sdelphij		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1510194916Sdelphij		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1511166255Sdelphij			goto unexpected_EOF;
1512166255Sdelphij		if (rv == -1) {
1513166255Sdelphij			if (!fflag)
1514166255Sdelphij				maybe_warn("can't read %s", file);
1515166255Sdelphij			goto lose;
1516166255Sdelphij		}
1517326559Sdelphij		infile_newdata(rv);
1518336661Sdelphij		timestamp = le32dec(&ts[0]);
1519166255Sdelphij
1520166255Sdelphij		if (header1[3] & ORIG_NAME) {
1521281540Sdelphij			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
1522166255Sdelphij			if (rbytes < 0) {
1523166255Sdelphij				maybe_warn("can't read %s", file);
1524166255Sdelphij				goto lose;
1525166255Sdelphij			}
1526281540Sdelphij			if (name[0] != '\0') {
1527281500Sdelphij				char *dp, *nf;
1528281500Sdelphij
1529281540Sdelphij				/* Make sure that name is NUL-terminated */
1530281540Sdelphij				name[rbytes] = '\0';
1531281540Sdelphij
1532281500Sdelphij				/* strip saved directory name */
1533281500Sdelphij				nf = strrchr(name, '/');
1534281500Sdelphij				if (nf == NULL)
1535281500Sdelphij					nf = name;
1536281500Sdelphij				else
1537281500Sdelphij					nf++;
1538281500Sdelphij
1539166255Sdelphij				/* preserve original directory name */
1540281500Sdelphij				dp = strrchr(file, '/');
1541166255Sdelphij				if (dp == NULL)
1542166255Sdelphij					dp = file;
1543166255Sdelphij				else
1544166255Sdelphij					dp++;
1545166255Sdelphij				snprintf(outfile, outsize, "%.*s%.*s",
1546326559Sdelphij						(int) (dp - file),
1547281500Sdelphij						file, (int) rbytes, nf);
1548166255Sdelphij			}
1549166255Sdelphij		}
1550166255Sdelphij	}
1551166255Sdelphij#endif
1552166255Sdelphij	lseek(fd, 0, SEEK_SET);
1553166255Sdelphij
1554166255Sdelphij	if (cflag == 0 || lflag) {
1555166255Sdelphij#ifndef SMALL
1556166255Sdelphij		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1557312072Skib			maybe_warnx("%s has %ju other links -- skipping",
1558312072Skib			    file, (uintmax_t)isb.st_nlink - 1);
1559166255Sdelphij			goto lose;
1560166255Sdelphij		}
1561166255Sdelphij		if (nflag == 0 && timestamp)
1562166255Sdelphij			isb.st_mtime = timestamp;
1563166255Sdelphij		if (check_outfile(outfile) == 0)
1564166255Sdelphij			goto lose;
1565166255Sdelphij#endif
1566166255Sdelphij	}
1567166255Sdelphij
1568326559Sdelphij	if (cflag)
1569326559Sdelphij		zfd = STDOUT_FILENO;
1570326559Sdelphij	else if (lflag)
1571326559Sdelphij		zfd = -1;
1572326559Sdelphij	else {
1573166255Sdelphij		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1574166255Sdelphij		if (zfd == STDOUT_FILENO) {
1575166255Sdelphij			/* We won't close STDOUT_FILENO later... */
1576166255Sdelphij			zfd = dup(zfd);
1577166255Sdelphij			close(STDOUT_FILENO);
1578166255Sdelphij		}
1579166255Sdelphij		if (zfd == -1) {
1580166255Sdelphij			maybe_warn("can't open %s", outfile);
1581166255Sdelphij			goto lose;
1582166255Sdelphij		}
1583207247Sdelphij		remove_file = outfile;
1584326559Sdelphij	}
1585166255Sdelphij
1586226184Sdelphij	switch (method) {
1587166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1588226184Sdelphij	case FT_BZIP2:
1589166255Sdelphij		/* XXX */
1590166255Sdelphij		if (lflag) {
1591166255Sdelphij			maybe_warnx("no -l with bzip2 files");
1592166255Sdelphij			goto lose;
1593166255Sdelphij		}
1594166255Sdelphij
1595166255Sdelphij		size = unbzip2(fd, zfd, NULL, 0, NULL);
1596226184Sdelphij		break;
1597166255Sdelphij#endif
1598166255Sdelphij
1599166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1600226184Sdelphij	case FT_Z: {
1601166255Sdelphij		FILE *in, *out;
1602166255Sdelphij
1603166255Sdelphij		/* XXX */
1604166255Sdelphij		if (lflag) {
1605166255Sdelphij			maybe_warnx("no -l with Lempel-Ziv files");
1606166255Sdelphij			goto lose;
1607166255Sdelphij		}
1608166255Sdelphij
1609166255Sdelphij		if ((in = zdopen(fd)) == NULL) {
1610166255Sdelphij			maybe_warn("zdopen for read: %s", file);
1611166255Sdelphij			goto lose;
1612166255Sdelphij		}
1613166255Sdelphij
1614166255Sdelphij		out = fdopen(dup(zfd), "w");
1615166255Sdelphij		if (out == NULL) {
1616166255Sdelphij			maybe_warn("fdopen for write: %s", outfile);
1617166255Sdelphij			fclose(in);
1618166255Sdelphij			goto lose;
1619166255Sdelphij		}
1620166255Sdelphij
1621166255Sdelphij		size = zuncompress(in, out, NULL, 0, NULL);
1622166255Sdelphij		/* need to fclose() if ferror() is true... */
1623337521Sdelphij		error = ferror(in);
1624337521Sdelphij		if (error | fclose(in)) {
1625337521Sdelphij			if (error)
1626337521Sdelphij				maybe_warn("failed infile");
1627337521Sdelphij			else
1628337521Sdelphij				maybe_warn("failed infile fclose");
1629337521Sdelphij			if (cflag == 0)
1630337521Sdelphij				unlink(outfile);
1631166255Sdelphij			(void)fclose(out);
1632337521Sdelphij			goto lose;
1633166255Sdelphij		}
1634166255Sdelphij		if (fclose(out) != 0) {
1635166255Sdelphij			maybe_warn("failed outfile fclose");
1636337521Sdelphij			if (cflag == 0)
1637337521Sdelphij				unlink(outfile);
1638166255Sdelphij			goto lose;
1639166255Sdelphij		}
1640226184Sdelphij		break;
1641226184Sdelphij	}
1642166255Sdelphij#endif
1643166255Sdelphij
1644194579Sdelphij#ifndef NO_PACK_SUPPORT
1645226184Sdelphij	case FT_PACK:
1646194579Sdelphij		if (lflag) {
1647194579Sdelphij			maybe_warnx("no -l with packed files");
1648194579Sdelphij			goto lose;
1649194579Sdelphij		}
1650194579Sdelphij
1651194579Sdelphij		size = unpack(fd, zfd, NULL, 0, NULL);
1652226184Sdelphij		break;
1653194579Sdelphij#endif
1654194579Sdelphij
1655226184Sdelphij#ifndef NO_XZ_SUPPORT
1656226184Sdelphij	case FT_XZ:
1657226184Sdelphij		if (lflag) {
1658343251Sdelphij			size = unxz_len(fd);
1659343251Sdelphij			print_list_out(in_size, size, file);
1660343251Sdelphij			return -1;
1661226184Sdelphij		}
1662226184Sdelphij		size = unxz(fd, zfd, NULL, 0, NULL);
1663226184Sdelphij		break;
1664226184Sdelphij#endif
1665226184Sdelphij
1666343251Sdelphij#ifndef NO_LZ_SUPPORT
1667343251Sdelphij	case FT_LZ:
1668343251Sdelphij		if (lflag) {
1669343251Sdelphij			maybe_warnx("no -l with lzip files");
1670343251Sdelphij			goto lose;
1671343251Sdelphij		}
1672343251Sdelphij		size = unlz(fd, zfd, NULL, 0, NULL);
1673343251Sdelphij		break;
1674343251Sdelphij#endif
1675166255Sdelphij#ifndef SMALL
1676226184Sdelphij	case FT_UNKNOWN:
1677166255Sdelphij		if (lflag) {
1678166255Sdelphij			maybe_warnx("no -l for unknown filetypes");
1679166255Sdelphij			goto lose;
1680166255Sdelphij		}
1681166255Sdelphij		size = cat_fd(NULL, 0, NULL, fd);
1682226184Sdelphij		break;
1683166255Sdelphij#endif
1684226184Sdelphij	default:
1685166255Sdelphij		if (lflag) {
1686326559Sdelphij			print_list(fd, in_size, outfile, isb.st_mtime);
1687166255Sdelphij			close(fd);
1688166255Sdelphij			return -1;	/* XXX */
1689166255Sdelphij		}
1690166255Sdelphij
1691166255Sdelphij		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1692226184Sdelphij		break;
1693166255Sdelphij	}
1694166255Sdelphij
1695166255Sdelphij	if (close(fd) != 0)
1696166255Sdelphij		maybe_warn("couldn't close input");
1697166255Sdelphij	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1698166255Sdelphij		maybe_warn("couldn't close output");
1699166255Sdelphij
1700166255Sdelphij	if (size == -1) {
1701166255Sdelphij		if (cflag == 0)
1702166255Sdelphij			unlink(outfile);
1703166255Sdelphij		maybe_warnx("%s: uncompress failed", file);
1704166255Sdelphij		return -1;
1705166255Sdelphij	}
1706166255Sdelphij
1707166255Sdelphij	/* if testing, or we uncompressed to stdout, this is all we need */
1708166255Sdelphij#ifndef SMALL
1709166255Sdelphij	if (tflag)
1710166255Sdelphij		return size;
1711166255Sdelphij#endif
1712166255Sdelphij	/* if we are uncompressing to stdin, don't remove the file. */
1713166255Sdelphij	if (cflag)
1714166255Sdelphij		return size;
1715166255Sdelphij
1716166255Sdelphij	/*
1717166255Sdelphij	 * if we create a file...
1718166255Sdelphij	 */
1719166255Sdelphij	/*
1720166255Sdelphij	 * if we can't stat the file don't remove the file.
1721166255Sdelphij	 */
1722166255Sdelphij
1723166255Sdelphij	ofd = open(outfile, O_RDWR, 0);
1724166255Sdelphij	if (ofd == -1) {
1725166255Sdelphij		maybe_warn("couldn't open (leaving original): %s",
1726166255Sdelphij			   outfile);
1727166255Sdelphij		return -1;
1728166255Sdelphij	}
1729166255Sdelphij	if (fstat(ofd, &osb) != 0) {
1730166255Sdelphij		maybe_warn("couldn't stat (leaving original): %s",
1731166255Sdelphij			   outfile);
1732166255Sdelphij		close(ofd);
1733166255Sdelphij		return -1;
1734166255Sdelphij	}
1735166255Sdelphij	if (osb.st_size != size) {
1736209017Sdelphij		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1737209017Sdelphij		    (uintmax_t)size, (uintmax_t)osb.st_size);
1738166255Sdelphij		close(ofd);
1739166255Sdelphij		unlink(outfile);
1740166255Sdelphij		return -1;
1741166255Sdelphij	}
1742166255Sdelphij#ifndef SMALL
1743166255Sdelphij	copymodes(ofd, &isb, outfile);
1744207247Sdelphij	remove_file = NULL;
1745166255Sdelphij#endif
1746166255Sdelphij	close(ofd);
1747207247Sdelphij	unlink_input(file, &isb);
1748166255Sdelphij	return size;
1749166255Sdelphij
1750166255Sdelphij    unexpected_EOF:
1751166255Sdelphij	maybe_warnx("%s: unexpected end of file", file);
1752166255Sdelphij    lose:
1753166255Sdelphij	if (fd != -1)
1754166255Sdelphij		close(fd);
1755166255Sdelphij	if (zfd != -1 && zfd != STDOUT_FILENO)
1756327191Sdelphij		close(zfd);
1757166255Sdelphij	return -1;
1758166255Sdelphij}
1759166255Sdelphij
1760166255Sdelphij#ifndef SMALL
1761326559Sdelphijstatic void
1762326559Sdelphijcheck_siginfo(void)
1763326559Sdelphij{
1764326559Sdelphij	if (print_info == 0)
1765326559Sdelphij		return;
1766326559Sdelphij	if (infile) {
1767326559Sdelphij		if (infile_total) {
1768326559Sdelphij			int pcent = (int)((100.0 * infile_current) / infile_total);
1769326559Sdelphij
1770326559Sdelphij			fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n",
1771326559Sdelphij				infile, (unsigned long long)infile_current,
1772326559Sdelphij				(unsigned long long)infile_total, pcent);
1773326559Sdelphij		} else
1774326559Sdelphij			fprintf(stderr, "%s: done %llu bytes\n",
1775326559Sdelphij				infile, (unsigned long long)infile_current);
1776326559Sdelphij	}
1777326559Sdelphij	print_info = 0;
1778326559Sdelphij}
1779326559Sdelphij
1780166255Sdelphijstatic off_t
1781166255Sdelphijcat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1782166255Sdelphij{
1783166255Sdelphij	char buf[BUFLEN];
1784166255Sdelphij	off_t in_tot;
1785166255Sdelphij	ssize_t w;
1786166255Sdelphij
1787166255Sdelphij	in_tot = count;
1788326559Sdelphij	w = write_retry(STDOUT_FILENO, prepend, count);
1789166255Sdelphij	if (w == -1 || (size_t)w != count) {
1790166255Sdelphij		maybe_warn("write to stdout");
1791166255Sdelphij		return -1;
1792166255Sdelphij	}
1793166255Sdelphij	for (;;) {
1794166255Sdelphij		ssize_t rv;
1795166255Sdelphij
1796166255Sdelphij		rv = read(fd, buf, sizeof buf);
1797166255Sdelphij		if (rv == 0)
1798166255Sdelphij			break;
1799166255Sdelphij		if (rv < 0) {
1800166255Sdelphij			maybe_warn("read from fd %d", fd);
1801166255Sdelphij			break;
1802166255Sdelphij		}
1803326559Sdelphij		infile_newdata(rv);
1804166255Sdelphij
1805326559Sdelphij		if (write_retry(STDOUT_FILENO, buf, rv) != rv) {
1806166255Sdelphij			maybe_warn("write to stdout");
1807166255Sdelphij			break;
1808166255Sdelphij		}
1809166255Sdelphij		in_tot += rv;
1810166255Sdelphij	}
1811166255Sdelphij
1812166255Sdelphij	if (gsizep)
1813166255Sdelphij		*gsizep = in_tot;
1814166255Sdelphij	return (in_tot);
1815166255Sdelphij}
1816166255Sdelphij#endif
1817166255Sdelphij
1818166255Sdelphijstatic void
1819166255Sdelphijhandle_stdin(void)
1820166255Sdelphij{
1821326559Sdelphij	struct stat isb;
1822166255Sdelphij	unsigned char header1[4];
1823326559Sdelphij	size_t in_size;
1824166255Sdelphij	off_t usize, gsize;
1825166255Sdelphij	enum filetype method;
1826166255Sdelphij	ssize_t bytes_read;
1827166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1828166255Sdelphij	FILE *in;
1829166255Sdelphij#endif
1830166255Sdelphij
1831166255Sdelphij#ifndef SMALL
1832166255Sdelphij	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1833166255Sdelphij		maybe_warnx("standard input is a terminal -- ignoring");
1834326559Sdelphij		goto out;
1835166255Sdelphij	}
1836166255Sdelphij#endif
1837166255Sdelphij
1838326559Sdelphij	if (fstat(STDIN_FILENO, &isb) < 0) {
1839326559Sdelphij		maybe_warn("fstat");
1840326559Sdelphij		goto out;
1841326559Sdelphij	}
1842326559Sdelphij	if (S_ISREG(isb.st_mode))
1843326559Sdelphij		in_size = isb.st_size;
1844326559Sdelphij	else
1845326559Sdelphij		in_size = 0;
1846326559Sdelphij	infile_set("(stdin)", in_size);
1847326559Sdelphij
1848166255Sdelphij	if (lflag) {
1849326559Sdelphij		print_list(STDIN_FILENO, in_size, infile, isb.st_mtime);
1850326559Sdelphij		goto out;
1851166255Sdelphij	}
1852166255Sdelphij
1853166255Sdelphij	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1854166255Sdelphij	if (bytes_read == -1) {
1855166255Sdelphij		maybe_warn("can't read stdin");
1856326559Sdelphij		goto out;
1857166255Sdelphij	} else if (bytes_read != sizeof(header1)) {
1858166255Sdelphij		maybe_warnx("(stdin): unexpected end of file");
1859326559Sdelphij		goto out;
1860166255Sdelphij	}
1861166255Sdelphij
1862166255Sdelphij	method = file_gettype(header1);
1863166255Sdelphij	switch (method) {
1864166255Sdelphij	default:
1865166255Sdelphij#ifndef SMALL
1866166255Sdelphij		if (fflag == 0) {
1867166255Sdelphij			maybe_warnx("unknown compression format");
1868326559Sdelphij			goto out;
1869166255Sdelphij		}
1870166255Sdelphij		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1871166255Sdelphij		break;
1872166255Sdelphij#endif
1873166255Sdelphij	case FT_GZIP:
1874326559Sdelphij		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1875166255Sdelphij			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1876166255Sdelphij		break;
1877166255Sdelphij#ifndef NO_BZIP2_SUPPORT
1878166255Sdelphij	case FT_BZIP2:
1879166255Sdelphij		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1880166255Sdelphij				(char *)header1, sizeof header1, &gsize);
1881166255Sdelphij		break;
1882166255Sdelphij#endif
1883166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
1884166255Sdelphij	case FT_Z:
1885166255Sdelphij		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1886166255Sdelphij			maybe_warnx("zopen of stdin");
1887326559Sdelphij			goto out;
1888166255Sdelphij		}
1889166255Sdelphij
1890226184Sdelphij		usize = zuncompress(in, stdout, (char *)header1,
1891226184Sdelphij		    sizeof header1, &gsize);
1892166255Sdelphij		fclose(in);
1893166255Sdelphij		break;
1894166255Sdelphij#endif
1895194579Sdelphij#ifndef NO_PACK_SUPPORT
1896194579Sdelphij	case FT_PACK:
1897194579Sdelphij		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1898194579Sdelphij			       (char *)header1, sizeof header1, &gsize);
1899194579Sdelphij		break;
1900194579Sdelphij#endif
1901226184Sdelphij#ifndef NO_XZ_SUPPORT
1902226184Sdelphij	case FT_XZ:
1903226184Sdelphij		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1904226184Sdelphij			     (char *)header1, sizeof header1, &gsize);
1905226184Sdelphij		break;
1906226184Sdelphij#endif
1907343251Sdelphij#ifndef NO_LZ_SUPPORT
1908343251Sdelphij	case FT_LZ:
1909343251Sdelphij		usize = unlz(STDIN_FILENO, STDOUT_FILENO,
1910343251Sdelphij			     (char *)header1, sizeof header1, &gsize);
1911343251Sdelphij		break;
1912343251Sdelphij#endif
1913166255Sdelphij	}
1914166255Sdelphij
1915166255Sdelphij#ifndef SMALL
1916166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1917166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1918166255Sdelphij	if (vflag && tflag)
1919166255Sdelphij		print_test("(stdin)", usize != -1);
1920326559Sdelphij#else
1921326559Sdelphij	(void)&usize;
1922326559Sdelphij#endif
1923166255Sdelphij
1924326559Sdelphijout:
1925326559Sdelphij	infile_clear();
1926166255Sdelphij}
1927166255Sdelphij
1928166255Sdelphijstatic void
1929166255Sdelphijhandle_stdout(void)
1930166255Sdelphij{
1931326559Sdelphij	off_t gsize;
1932326559Sdelphij#ifndef SMALL
1933326559Sdelphij	off_t usize;
1934166255Sdelphij	struct stat sb;
1935166255Sdelphij	time_t systime;
1936166255Sdelphij	uint32_t mtime;
1937166255Sdelphij	int ret;
1938166255Sdelphij
1939326559Sdelphij	infile_set("(stdout)", 0);
1940326559Sdelphij
1941166255Sdelphij	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1942166255Sdelphij		maybe_warnx("standard output is a terminal -- ignoring");
1943166255Sdelphij		return;
1944166255Sdelphij	}
1945326559Sdelphij
1946273507Sdelphij	/* If stdin is a file use its mtime, otherwise use current time */
1947166255Sdelphij	ret = fstat(STDIN_FILENO, &sb);
1948166255Sdelphij	if (ret < 0) {
1949166255Sdelphij		maybe_warn("Can't stat stdin");
1950166255Sdelphij		return;
1951166255Sdelphij	}
1952166255Sdelphij
1953326559Sdelphij	if (S_ISREG(sb.st_mode)) {
1954326559Sdelphij		infile_set("(stdout)", sb.st_size);
1955166255Sdelphij		mtime = (uint32_t)sb.st_mtime;
1956326559Sdelphij	} else {
1957166255Sdelphij		systime = time(NULL);
1958166255Sdelphij		if (systime == -1) {
1959166255Sdelphij			maybe_warn("time");
1960166255Sdelphij			return;
1961326559Sdelphij		}
1962166255Sdelphij		mtime = (uint32_t)systime;
1963166255Sdelphij	}
1964166255Sdelphij
1965326559Sdelphij	usize =
1966326559Sdelphij#endif
1967326559Sdelphij		gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1968166255Sdelphij#ifndef SMALL
1969166255Sdelphij        if (vflag && !tflag && usize != -1 && gsize != -1)
1970166255Sdelphij		print_verbage(NULL, NULL, usize, gsize);
1971326559Sdelphij#endif
1972166255Sdelphij}
1973166255Sdelphij
1974166255Sdelphij/* do what is asked for, for the path name */
1975166255Sdelphijstatic void
1976166255Sdelphijhandle_pathname(char *path)
1977166255Sdelphij{
1978166255Sdelphij	char *opath = path, *s = NULL;
1979166255Sdelphij	ssize_t len;
1980166255Sdelphij	int slen;
1981166255Sdelphij	struct stat sb;
1982166255Sdelphij
1983166255Sdelphij	/* check for stdout/stdin */
1984166255Sdelphij	if (path[0] == '-' && path[1] == '\0') {
1985166255Sdelphij		if (dflag)
1986166255Sdelphij			handle_stdin();
1987166255Sdelphij		else
1988166255Sdelphij			handle_stdout();
1989166255Sdelphij		return;
1990166255Sdelphij	}
1991166255Sdelphij
1992166255Sdelphijretry:
1993222287Sdelphij	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1994222287Sdelphij	    lstat(path, &sb) != 0)) {
1995166255Sdelphij		/* lets try <path>.gz if we're decompressing */
1996166255Sdelphij		if (dflag && s == NULL && errno == ENOENT) {
1997166255Sdelphij			len = strlen(path);
1998166255Sdelphij			slen = suffixes[0].ziplen;
1999166255Sdelphij			s = malloc(len + slen + 1);
2000166255Sdelphij			if (s == NULL)
2001166255Sdelphij				maybe_err("malloc");
2002166255Sdelphij			memcpy(s, path, len);
2003166255Sdelphij			memcpy(s + len, suffixes[0].zipped, slen + 1);
2004166255Sdelphij			path = s;
2005166255Sdelphij			goto retry;
2006166255Sdelphij		}
2007166255Sdelphij		maybe_warn("can't stat: %s", opath);
2008166255Sdelphij		goto out;
2009166255Sdelphij	}
2010166255Sdelphij
2011166255Sdelphij	if (S_ISDIR(sb.st_mode)) {
2012166255Sdelphij#ifndef SMALL
2013166255Sdelphij		if (rflag)
2014166255Sdelphij			handle_dir(path);
2015166255Sdelphij		else
2016166255Sdelphij#endif
2017166255Sdelphij			maybe_warnx("%s is a directory", path);
2018166255Sdelphij		goto out;
2019166255Sdelphij	}
2020166255Sdelphij
2021166255Sdelphij	if (S_ISREG(sb.st_mode))
2022166255Sdelphij		handle_file(path, &sb);
2023166255Sdelphij	else
2024166255Sdelphij		maybe_warnx("%s is not a regular file", path);
2025166255Sdelphij
2026166255Sdelphijout:
2027166255Sdelphij	if (s)
2028166255Sdelphij		free(s);
2029166255Sdelphij}
2030166255Sdelphij
2031166255Sdelphij/* compress/decompress a file */
2032166255Sdelphijstatic void
2033166255Sdelphijhandle_file(char *file, struct stat *sbp)
2034166255Sdelphij{
2035166255Sdelphij	off_t usize, gsize;
2036166255Sdelphij	char	outfile[PATH_MAX];
2037166255Sdelphij
2038326559Sdelphij	infile_set(file, sbp->st_size);
2039166255Sdelphij	if (dflag) {
2040166255Sdelphij		usize = file_uncompress(file, outfile, sizeof(outfile));
2041166255Sdelphij#ifndef SMALL
2042166255Sdelphij		if (vflag && tflag)
2043166255Sdelphij			print_test(file, usize != -1);
2044166255Sdelphij#endif
2045166255Sdelphij		if (usize == -1)
2046166255Sdelphij			return;
2047166255Sdelphij		gsize = sbp->st_size;
2048166255Sdelphij	} else {
2049166255Sdelphij		gsize = file_compress(file, outfile, sizeof(outfile));
2050166255Sdelphij		if (gsize == -1)
2051166255Sdelphij			return;
2052166255Sdelphij		usize = sbp->st_size;
2053166255Sdelphij	}
2054326559Sdelphij	infile_clear();
2055166255Sdelphij
2056166255Sdelphij#ifndef SMALL
2057166255Sdelphij	if (vflag && !tflag)
2058166255Sdelphij		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
2059166255Sdelphij#endif
2060166255Sdelphij}
2061166255Sdelphij
2062166255Sdelphij#ifndef SMALL
2063166255Sdelphij/* this is used with -r to recursively descend directories */
2064166255Sdelphijstatic void
2065166255Sdelphijhandle_dir(char *dir)
2066166255Sdelphij{
2067166255Sdelphij	char *path_argv[2];
2068166255Sdelphij	FTS *fts;
2069166255Sdelphij	FTSENT *entry;
2070166255Sdelphij
2071166255Sdelphij	path_argv[0] = dir;
2072166255Sdelphij	path_argv[1] = 0;
2073171389Sdelphij	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
2074166255Sdelphij	if (fts == NULL) {
2075166255Sdelphij		warn("couldn't fts_open %s", dir);
2076166255Sdelphij		return;
2077166255Sdelphij	}
2078166255Sdelphij
2079166255Sdelphij	while ((entry = fts_read(fts))) {
2080166255Sdelphij		switch(entry->fts_info) {
2081166255Sdelphij		case FTS_D:
2082166255Sdelphij		case FTS_DP:
2083166255Sdelphij			continue;
2084166255Sdelphij
2085166255Sdelphij		case FTS_DNR:
2086166255Sdelphij		case FTS_ERR:
2087166255Sdelphij		case FTS_NS:
2088166255Sdelphij			maybe_warn("%s", entry->fts_path);
2089166255Sdelphij			continue;
2090166255Sdelphij		case FTS_F:
2091171389Sdelphij			handle_file(entry->fts_path, entry->fts_statp);
2092166255Sdelphij		}
2093166255Sdelphij	}
2094166255Sdelphij	(void)fts_close(fts);
2095166255Sdelphij}
2096166255Sdelphij#endif
2097166255Sdelphij
2098166255Sdelphij/* print a ratio - size reduction as a fraction of uncompressed size */
2099166255Sdelphijstatic void
2100166255Sdelphijprint_ratio(off_t in, off_t out, FILE *where)
2101166255Sdelphij{
2102166255Sdelphij	int percent10;	/* 10 * percent */
2103166255Sdelphij	off_t diff;
2104166255Sdelphij	char buff[8];
2105166255Sdelphij	int len;
2106166255Sdelphij
2107166255Sdelphij	diff = in - out/2;
2108326559Sdelphij	if (in == 0 && out == 0)
2109326559Sdelphij		percent10 = 0;
2110326559Sdelphij	else if (diff < 0)
2111166255Sdelphij		/*
2112166255Sdelphij		 * Output is more than double size of input! print -99.9%
2113166255Sdelphij		 * Quite possibly we've failed to get the original size.
2114166255Sdelphij		 */
2115166255Sdelphij		percent10 = -999;
2116166255Sdelphij	else {
2117166255Sdelphij		/*
2118166255Sdelphij		 * We only need 12 bits of result from the final division,
2119166255Sdelphij		 * so reduce the values until a 32bit division will suffice.
2120166255Sdelphij		 */
2121166255Sdelphij		while (in > 0x100000) {
2122166255Sdelphij			diff >>= 1;
2123166255Sdelphij			in >>= 1;
2124166255Sdelphij		}
2125166255Sdelphij		if (in != 0)
2126166255Sdelphij			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2127166255Sdelphij		else
2128166255Sdelphij			percent10 = 0;
2129166255Sdelphij	}
2130166255Sdelphij
2131166255Sdelphij	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2132166255Sdelphij	/* Move the '.' to before the last digit */
2133166255Sdelphij	buff[len - 1] = buff[len - 2];
2134166255Sdelphij	buff[len - 2] = '.';
2135166255Sdelphij	fprintf(where, "%5s%%", buff);
2136166255Sdelphij}
2137166255Sdelphij
2138166255Sdelphij#ifndef SMALL
2139166255Sdelphij/* print compression statistics, and the new name (if there is one!) */
2140166255Sdelphijstatic void
2141166255Sdelphijprint_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
2142166255Sdelphij{
2143166255Sdelphij	if (file)
2144166255Sdelphij		fprintf(stderr, "%s:%s  ", file,
2145166255Sdelphij		    strlen(file) < 7 ? "\t\t" : "\t");
2146166255Sdelphij	print_ratio(usize, gsize, stderr);
2147166255Sdelphij	if (nfile)
2148166255Sdelphij		fprintf(stderr, " -- replaced with %s", nfile);
2149166255Sdelphij	fprintf(stderr, "\n");
2150166255Sdelphij	fflush(stderr);
2151166255Sdelphij}
2152166255Sdelphij
2153166255Sdelphij/* print test results */
2154166255Sdelphijstatic void
2155166255Sdelphijprint_test(const char *file, int ok)
2156166255Sdelphij{
2157166255Sdelphij
2158166255Sdelphij	if (exit_value == 0 && ok == 0)
2159166255Sdelphij		exit_value = 1;
2160166255Sdelphij	fprintf(stderr, "%s:%s  %s\n", file,
2161166255Sdelphij	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2162166255Sdelphij	fflush(stderr);
2163166255Sdelphij}
2164166255Sdelphij#endif
2165166255Sdelphij
2166166255Sdelphij/* print a file's info ala --list */
2167166255Sdelphij/* eg:
2168166255Sdelphij  compressed uncompressed  ratio uncompressed_name
2169166255Sdelphij      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2170166255Sdelphij*/
2171166255Sdelphijstatic void
2172166255Sdelphijprint_list(int fd, off_t out, const char *outfile, time_t ts)
2173166255Sdelphij{
2174166255Sdelphij	static int first = 1;
2175166255Sdelphij#ifndef SMALL
2176166255Sdelphij	static off_t in_tot, out_tot;
2177166255Sdelphij	uint32_t crc = 0;
2178166255Sdelphij#endif
2179166255Sdelphij	off_t in = 0, rv;
2180166255Sdelphij
2181166255Sdelphij	if (first) {
2182166255Sdelphij#ifndef SMALL
2183166255Sdelphij		if (vflag)
2184166255Sdelphij			printf("method  crc     date  time  ");
2185166255Sdelphij#endif
2186166255Sdelphij		if (qflag == 0)
2187166255Sdelphij			printf("  compressed uncompressed  "
2188166255Sdelphij			       "ratio uncompressed_name\n");
2189166255Sdelphij	}
2190166255Sdelphij	first = 0;
2191166255Sdelphij
2192166255Sdelphij	/* print totals? */
2193166255Sdelphij#ifndef SMALL
2194166255Sdelphij	if (fd == -1) {
2195166255Sdelphij		in = in_tot;
2196166255Sdelphij		out = out_tot;
2197166255Sdelphij	} else
2198166255Sdelphij#endif
2199166255Sdelphij	{
2200166255Sdelphij		/* read the last 4 bytes - this is the uncompressed size */
2201166255Sdelphij		rv = lseek(fd, (off_t)(-8), SEEK_END);
2202166255Sdelphij		if (rv != -1) {
2203166255Sdelphij			unsigned char buf[8];
2204166255Sdelphij			uint32_t usize;
2205166255Sdelphij
2206166255Sdelphij			rv = read(fd, (char *)buf, sizeof(buf));
2207166255Sdelphij			if (rv == -1)
2208166255Sdelphij				maybe_warn("read of uncompressed size");
2209166255Sdelphij			else if (rv != sizeof(buf))
2210166255Sdelphij				maybe_warnx("read of uncompressed size");
2211166255Sdelphij
2212166255Sdelphij			else {
2213336661Sdelphij				usize = le32dec(&buf[4]);
2214166255Sdelphij				in = (off_t)usize;
2215166255Sdelphij#ifndef SMALL
2216336661Sdelphij				crc = le32dec(&buf[0]);
2217166255Sdelphij#endif
2218166255Sdelphij			}
2219166255Sdelphij		}
2220166255Sdelphij	}
2221166255Sdelphij
2222166255Sdelphij#ifndef SMALL
2223166255Sdelphij	if (vflag && fd == -1)
2224166255Sdelphij		printf("                            ");
2225166255Sdelphij	else if (vflag) {
2226166255Sdelphij		char *date = ctime(&ts);
2227166255Sdelphij
2228166255Sdelphij		/* skip the day, 1/100th second, and year */
2229166255Sdelphij		date += 4;
2230166255Sdelphij		date[12] = 0;
2231166255Sdelphij		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2232166255Sdelphij	}
2233166255Sdelphij	in_tot += in;
2234166255Sdelphij	out_tot += out;
2235194508Sdelphij#else
2236194508Sdelphij	(void)&ts;	/* XXX */
2237166255Sdelphij#endif
2238343251Sdelphij	print_list_out(out, in, outfile);
2239343251Sdelphij}
2240343251Sdelphij
2241343251Sdelphijstatic void
2242343251Sdelphijprint_list_out(off_t out, off_t in, const char *outfile)
2243343251Sdelphij{
2244166255Sdelphij	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2245166255Sdelphij	print_ratio(in, out, stdout);
2246166255Sdelphij	printf(" %s\n", outfile);
2247166255Sdelphij}
2248166255Sdelphij
2249166255Sdelphij/* display the usage of NetBSD gzip */
2250166255Sdelphijstatic void
2251166255Sdelphijusage(void)
2252166255Sdelphij{
2253166255Sdelphij
2254166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2255166255Sdelphij	fprintf(stderr,
2256171389Sdelphij#ifdef SMALL
2257171389Sdelphij    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2258171389Sdelphij#else
2259171389Sdelphij    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2260166255Sdelphij    " -1 --fast            fastest (worst) compression\n"
2261166255Sdelphij    " -2 .. -8             set compression level\n"
2262166255Sdelphij    " -9 --best            best (slowest) compression\n"
2263166255Sdelphij    " -c --stdout          write to stdout, keep original files\n"
2264166255Sdelphij    "    --to-stdout\n"
2265166255Sdelphij    " -d --decompress      uncompress files\n"
2266166255Sdelphij    "    --uncompress\n"
2267166255Sdelphij    " -f --force           force overwriting & compress links\n"
2268166255Sdelphij    " -h --help            display this help\n"
2269170053Sdelphij    " -k --keep            don't delete input files during operation\n"
2270166255Sdelphij    " -l --list            list compressed file contents\n"
2271166255Sdelphij    " -N --name            save or restore original file name and time stamp\n"
2272166255Sdelphij    " -n --no-name         don't save original file name or time stamp\n"
2273166255Sdelphij    " -q --quiet           output no warnings\n"
2274166255Sdelphij    " -r --recursive       recursively compress files in directories\n"
2275166255Sdelphij    " -S .suf              use suffix .suf instead of .gz\n"
2276166255Sdelphij    "    --suffix .suf\n"
2277166255Sdelphij    " -t --test            test compressed file\n"
2278166255Sdelphij    " -V --version         display program version\n"
2279166255Sdelphij    " -v --verbose         print extra statistics\n",
2280166255Sdelphij#endif
2281166255Sdelphij	    getprogname());
2282166255Sdelphij	exit(0);
2283166255Sdelphij}
2284166255Sdelphij
2285166255Sdelphij#ifndef SMALL
2286166255Sdelphij/* display the license information of FreeBSD gzip */
2287166255Sdelphijstatic void
2288166255Sdelphijdisplay_license(void)
2289166255Sdelphij{
2290166255Sdelphij
2291281500Sdelphij	fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
2292166255Sdelphij	fprintf(stderr, "%s\n", gzip_copyright);
2293166255Sdelphij	exit(0);
2294166255Sdelphij}
2295166255Sdelphij#endif
2296166255Sdelphij
2297166255Sdelphij/* display the version of NetBSD gzip */
2298166255Sdelphijstatic void
2299166255Sdelphijdisplay_version(void)
2300166255Sdelphij{
2301166255Sdelphij
2302166255Sdelphij	fprintf(stderr, "%s\n", gzip_version);
2303166255Sdelphij	exit(0);
2304166255Sdelphij}
2305166255Sdelphij
2306166255Sdelphij#ifndef NO_BZIP2_SUPPORT
2307166255Sdelphij#include "unbzip2.c"
2308166255Sdelphij#endif
2309166255Sdelphij#ifndef NO_COMPRESS_SUPPORT
2310166255Sdelphij#include "zuncompress.c"
2311166255Sdelphij#endif
2312194579Sdelphij#ifndef NO_PACK_SUPPORT
2313194579Sdelphij#include "unpack.c"
2314194579Sdelphij#endif
2315226184Sdelphij#ifndef NO_XZ_SUPPORT
2316226184Sdelphij#include "unxz.c"
2317226184Sdelphij#endif
2318343251Sdelphij#ifndef NO_LZ_SUPPORT
2319343251Sdelphij#include "unlz.c"
2320343251Sdelphij#endif
2321166255Sdelphij
2322166255Sdelphijstatic ssize_t
2323166255Sdelphijread_retry(int fd, void *buf, size_t sz)
2324166255Sdelphij{
2325166255Sdelphij	char *cp = buf;
2326166255Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2327166255Sdelphij
2328166255Sdelphij	while (left > 0) {
2329166255Sdelphij		ssize_t ret;
2330166255Sdelphij
2331166255Sdelphij		ret = read(fd, cp, left);
2332166255Sdelphij		if (ret == -1) {
2333166255Sdelphij			return ret;
2334166255Sdelphij		} else if (ret == 0) {
2335166255Sdelphij			break; /* EOF */
2336166255Sdelphij		}
2337166255Sdelphij		cp += ret;
2338166255Sdelphij		left -= ret;
2339166255Sdelphij	}
2340166255Sdelphij
2341166255Sdelphij	return sz - left;
2342166255Sdelphij}
2343326559Sdelphij
2344326559Sdelphijstatic ssize_t
2345326559Sdelphijwrite_retry(int fd, const void *buf, size_t sz)
2346326559Sdelphij{
2347326559Sdelphij	const char *cp = buf;
2348326559Sdelphij	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2349326559Sdelphij
2350326559Sdelphij	while (left > 0) {
2351326559Sdelphij		ssize_t ret;
2352326559Sdelphij
2353326559Sdelphij		ret = write(fd, cp, left);
2354326559Sdelphij		if (ret == -1) {
2355326559Sdelphij			return ret;
2356326559Sdelphij		} else if (ret == 0) {
2357326559Sdelphij			abort();	/* Can't happen */
2358326559Sdelphij		}
2359326559Sdelphij		cp += ret;
2360326559Sdelphij		left -= ret;
2361326559Sdelphij	}
2362326559Sdelphij
2363326559Sdelphij	return sz - left;
2364326559Sdelphij}
2365