gzip.c revision 327191
1/*	$NetBSD: gzip.c,v 1.112 2017/08/23 13:04:17 christos Exp $	*/
2
3/*-
4 * Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008, 2009, 2010, 2011, 2015, 2017
5 *    Matthew R. Green
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31#include <sys/cdefs.h>
32#ifndef lint
33__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008,\
34 2009, 2010, 2011, 2015, 2017 Matthew R. Green.  All rights reserved.");
35__FBSDID("$FreeBSD: stable/11/usr.bin/gzip/gzip.c 327191 2017-12-26 08:32:02Z delphij $");
36#endif /* not lint */
37
38/*
39 * gzip.c -- GPL free gzip using zlib.
40 *
41 * RFC 1950 covers the zlib format
42 * RFC 1951 covers the deflate format
43 * RFC 1952 covers the gzip format
44 *
45 * TODO:
46 *	- use mmap where possible
47 *	- make bzip2/compress -v/-t/-l support work as well as possible
48 */
49
50#include <sys/param.h>
51#include <sys/stat.h>
52#include <sys/time.h>
53
54#include <inttypes.h>
55#include <unistd.h>
56#include <stdio.h>
57#include <string.h>
58#include <stdlib.h>
59#include <err.h>
60#include <errno.h>
61#include <fcntl.h>
62#include <zlib.h>
63#include <fts.h>
64#include <libgen.h>
65#include <stdarg.h>
66#include <getopt.h>
67#include <time.h>
68
69/* what type of file are we dealing with */
70enum filetype {
71	FT_GZIP,
72#ifndef NO_BZIP2_SUPPORT
73	FT_BZIP2,
74#endif
75#ifndef NO_COMPRESS_SUPPORT
76	FT_Z,
77#endif
78#ifndef NO_PACK_SUPPORT
79	FT_PACK,
80#endif
81#ifndef NO_XZ_SUPPORT
82	FT_XZ,
83#endif
84	FT_LAST,
85	FT_UNKNOWN
86};
87
88#ifndef NO_BZIP2_SUPPORT
89#include <bzlib.h>
90
91#define BZ2_SUFFIX	".bz2"
92#define BZIP2_MAGIC	"BZh"
93#endif
94
95#ifndef NO_COMPRESS_SUPPORT
96#define Z_SUFFIX	".Z"
97#define Z_MAGIC		"\037\235"
98#endif
99
100#ifndef NO_PACK_SUPPORT
101#define PACK_MAGIC	"\037\036"
102#endif
103
104#ifndef NO_XZ_SUPPORT
105#include <lzma.h>
106#define XZ_SUFFIX	".xz"
107#define XZ_MAGIC	"\3757zXZ"
108#endif
109
110#define GZ_SUFFIX	".gz"
111
112#define BUFLEN		(64 * 1024)
113
114#define GZIP_MAGIC0	0x1F
115#define GZIP_MAGIC1	0x8B
116#define GZIP_OMAGIC1	0x9E
117
118#define GZIP_TIMESTAMP	(off_t)4
119#define GZIP_ORIGNAME	(off_t)10
120
121#define HEAD_CRC	0x02
122#define EXTRA_FIELD	0x04
123#define ORIG_NAME	0x08
124#define COMMENT		0x10
125
126#define OS_CODE		3	/* Unix */
127
128typedef struct {
129    const char	*zipped;
130    int		ziplen;
131    const char	*normal;	/* for unzip - must not be longer than zipped */
132} suffixes_t;
133static suffixes_t suffixes[] = {
134#define	SUFFIX(Z, N) {Z, sizeof Z - 1, N}
135	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S .xxx */
136#ifndef SMALL
137	SUFFIX(GZ_SUFFIX,	""),
138	SUFFIX(".z",		""),
139	SUFFIX("-gz",		""),
140	SUFFIX("-z",		""),
141	SUFFIX("_z",		""),
142	SUFFIX(".taz",		".tar"),
143	SUFFIX(".tgz",		".tar"),
144#ifndef NO_BZIP2_SUPPORT
145	SUFFIX(BZ2_SUFFIX,	""),
146	SUFFIX(".tbz",		".tar"),
147	SUFFIX(".tbz2",		".tar"),
148#endif
149#ifndef NO_COMPRESS_SUPPORT
150	SUFFIX(Z_SUFFIX,	""),
151#endif
152#ifndef NO_XZ_SUPPORT
153	SUFFIX(XZ_SUFFIX,	""),
154#endif
155	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S "" */
156#endif /* SMALL */
157#undef SUFFIX
158};
159#define NUM_SUFFIXES (nitems(suffixes))
160#define SUFFIX_MAXLEN	30
161
162static	const char	gzip_version[] = "FreeBSD gzip 20171121";
163
164#ifndef SMALL
165static	const char	gzip_copyright[] = \
166"   Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
167"   All rights reserved.\n"
168"\n"
169"   Redistribution and use in source and binary forms, with or without\n"
170"   modification, are permitted provided that the following conditions\n"
171"   are met:\n"
172"   1. Redistributions of source code must retain the above copyright\n"
173"      notice, this list of conditions and the following disclaimer.\n"
174"   2. Redistributions in binary form must reproduce the above copyright\n"
175"      notice, this list of conditions and the following disclaimer in the\n"
176"      documentation and/or other materials provided with the distribution.\n"
177"\n"
178"   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
179"   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
180"   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
181"   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
182"   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
183"   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
184"   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
185"   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
186"   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
187"   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
188"   SUCH DAMAGE.";
189#endif
190
191static	int	cflag;			/* stdout mode */
192static	int	dflag;			/* decompress mode */
193static	int	lflag;			/* list mode */
194static	int	numflag = 6;		/* gzip -1..-9 value */
195
196static	const char *remove_file = NULL;	/* file to be removed upon SIGINT */
197
198static	int	fflag;			/* force mode */
199#ifndef SMALL
200static	int	kflag;			/* don't delete input files */
201static	int	nflag;			/* don't save name/timestamp */
202static	int	Nflag;			/* don't restore name/timestamp */
203static	int	qflag;			/* quiet mode */
204static	int	rflag;			/* recursive mode */
205static	int	tflag;			/* test */
206static	int	vflag;			/* verbose mode */
207static	sig_atomic_t print_info = 0;
208#else
209#define		qflag	0
210#define		tflag	0
211#endif
212
213static	int	exit_value = 0;		/* exit value */
214
215static	const char *infile;		/* name of file coming in */
216
217static	void	maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
218#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
219    !defined(NO_XZ_SUPPORT)
220static	void	maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
221#endif
222static	void	maybe_warn(const char *fmt, ...) __printflike(1, 2);
223static	void	maybe_warnx(const char *fmt, ...) __printflike(1, 2);
224static	enum filetype file_gettype(u_char *);
225#ifdef SMALL
226#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
227#endif
228static	off_t	gz_compress(int, int, off_t *, const char *, uint32_t);
229static	off_t	gz_uncompress(int, int, char *, size_t, off_t *, const char *);
230static	off_t	file_compress(char *, char *, size_t);
231static	off_t	file_uncompress(char *, char *, size_t);
232static	void	handle_pathname(char *);
233static	void	handle_file(char *, struct stat *);
234static	void	handle_stdin(void);
235static	void	handle_stdout(void);
236static	void	print_ratio(off_t, off_t, FILE *);
237static	void	print_list(int fd, off_t, const char *, time_t);
238static	void	usage(void) __dead2;
239static	void	display_version(void) __dead2;
240#ifndef SMALL
241static	void	display_license(void);
242#endif
243static	const suffixes_t *check_suffix(char *, int);
244static	ssize_t	read_retry(int, void *, size_t);
245static	ssize_t	write_retry(int, const void *, size_t);
246
247#ifdef SMALL
248#define infile_set(f,t) infile_set(f)
249#endif
250static	void	infile_set(const char *newinfile, off_t total);
251
252#ifdef SMALL
253#define unlink_input(f, sb) unlink(f)
254#define check_siginfo() /* nothing */
255#define setup_signals() /* nothing */
256#define infile_newdata(t) /* nothing */
257#else
258static	off_t	infile_total;		/* total expected to read/write */
259static	off_t	infile_current;		/* current read/write */
260
261static	void	check_siginfo(void);
262static	off_t	cat_fd(unsigned char *, size_t, off_t *, int fd);
263static	void	prepend_gzip(char *, int *, char ***);
264static	void	handle_dir(char *);
265static	void	print_verbage(const char *, const char *, off_t, off_t);
266static	void	print_test(const char *, int);
267static	void	copymodes(int fd, const struct stat *, const char *file);
268static	int	check_outfile(const char *outfile);
269static	void	setup_signals(void);
270static	void	infile_newdata(size_t newdata);
271static	void	infile_clear(void);
272#endif
273
274#ifndef NO_BZIP2_SUPPORT
275static	off_t	unbzip2(int, int, char *, size_t, off_t *);
276#endif
277
278#ifndef NO_COMPRESS_SUPPORT
279static	FILE 	*zdopen(int);
280static	off_t	zuncompress(FILE *, FILE *, char *, size_t, off_t *);
281#endif
282
283#ifndef NO_PACK_SUPPORT
284static	off_t	unpack(int, int, char *, size_t, off_t *);
285#endif
286
287#ifndef NO_XZ_SUPPORT
288static	off_t	unxz(int, int, char *, size_t, off_t *);
289#endif
290
291#ifdef SMALL
292#define getopt_long(a,b,c,d,e) getopt(a,b,c)
293#else
294static const struct option longopts[] = {
295	{ "stdout",		no_argument,		0,	'c' },
296	{ "to-stdout",		no_argument,		0,	'c' },
297	{ "decompress",		no_argument,		0,	'd' },
298	{ "uncompress",		no_argument,		0,	'd' },
299	{ "force",		no_argument,		0,	'f' },
300	{ "help",		no_argument,		0,	'h' },
301	{ "keep",		no_argument,		0,	'k' },
302	{ "list",		no_argument,		0,	'l' },
303	{ "no-name",		no_argument,		0,	'n' },
304	{ "name",		no_argument,		0,	'N' },
305	{ "quiet",		no_argument,		0,	'q' },
306	{ "recursive",		no_argument,		0,	'r' },
307	{ "suffix",		required_argument,	0,	'S' },
308	{ "test",		no_argument,		0,	't' },
309	{ "verbose",		no_argument,		0,	'v' },
310	{ "version",		no_argument,		0,	'V' },
311	{ "fast",		no_argument,		0,	'1' },
312	{ "best",		no_argument,		0,	'9' },
313	{ "ascii",		no_argument,		0,	'a' },
314	{ "license",		no_argument,		0,	'L' },
315	{ NULL,			no_argument,		0,	0 },
316};
317#endif
318
319int
320main(int argc, char **argv)
321{
322	const char *progname = getprogname();
323#ifndef SMALL
324	char *gzip;
325	int len;
326#endif
327	int ch;
328
329	setup_signals();
330
331#ifndef SMALL
332	if ((gzip = getenv("GZIP")) != NULL)
333		prepend_gzip(gzip, &argc, &argv);
334#endif
335
336	/*
337	 * XXX
338	 * handle being called `gunzip', `zcat' and `gzcat'
339	 */
340	if (strcmp(progname, "gunzip") == 0)
341		dflag = 1;
342	else if (strcmp(progname, "zcat") == 0 ||
343		 strcmp(progname, "gzcat") == 0)
344		dflag = cflag = 1;
345
346#ifdef SMALL
347#define OPT_LIST "123456789cdhlV"
348#else
349#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
350#endif
351
352	while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
353		switch (ch) {
354		case '1': case '2': case '3':
355		case '4': case '5': case '6':
356		case '7': case '8': case '9':
357			numflag = ch - '0';
358			break;
359		case 'c':
360			cflag = 1;
361			break;
362		case 'd':
363			dflag = 1;
364			break;
365		case 'l':
366			lflag = 1;
367			dflag = 1;
368			break;
369		case 'V':
370			display_version();
371			/* NOTREACHED */
372#ifndef SMALL
373		case 'a':
374			fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
375			break;
376		case 'f':
377			fflag = 1;
378			break;
379		case 'k':
380			kflag = 1;
381			break;
382		case 'L':
383			display_license();
384			/* NOT REACHED */
385		case 'N':
386			nflag = 0;
387			Nflag = 1;
388			break;
389		case 'n':
390			nflag = 1;
391			Nflag = 0;
392			break;
393		case 'q':
394			qflag = 1;
395			break;
396		case 'r':
397			rflag = 1;
398			break;
399		case 'S':
400			len = strlen(optarg);
401			if (len != 0) {
402				if (len > SUFFIX_MAXLEN)
403					errx(1, "incorrect suffix: '%s': too long", optarg);
404				suffixes[0].zipped = optarg;
405				suffixes[0].ziplen = len;
406			} else {
407				suffixes[NUM_SUFFIXES - 1].zipped = "";
408				suffixes[NUM_SUFFIXES - 1].ziplen = 0;
409			}
410			break;
411		case 't':
412			cflag = 1;
413			tflag = 1;
414			dflag = 1;
415			break;
416		case 'v':
417			vflag = 1;
418			break;
419#endif
420		default:
421			usage();
422			/* NOTREACHED */
423		}
424	}
425	argv += optind;
426	argc -= optind;
427
428	if (argc == 0) {
429		if (dflag)	/* stdin mode */
430			handle_stdin();
431		else		/* stdout mode */
432			handle_stdout();
433	} else {
434		do {
435			handle_pathname(argv[0]);
436		} while (*++argv);
437	}
438#ifndef SMALL
439	if (qflag == 0 && lflag && argc > 1)
440		print_list(-1, 0, "(totals)", 0);
441#endif
442	exit(exit_value);
443}
444
445/* maybe print a warning */
446void
447maybe_warn(const char *fmt, ...)
448{
449	va_list ap;
450
451	if (qflag == 0) {
452		va_start(ap, fmt);
453		vwarn(fmt, ap);
454		va_end(ap);
455	}
456	if (exit_value == 0)
457		exit_value = 1;
458}
459
460/* ... without an errno. */
461void
462maybe_warnx(const char *fmt, ...)
463{
464	va_list ap;
465
466	if (qflag == 0) {
467		va_start(ap, fmt);
468		vwarnx(fmt, ap);
469		va_end(ap);
470	}
471	if (exit_value == 0)
472		exit_value = 1;
473}
474
475/* maybe print an error */
476void
477maybe_err(const char *fmt, ...)
478{
479	va_list ap;
480
481	if (qflag == 0) {
482		va_start(ap, fmt);
483		vwarn(fmt, ap);
484		va_end(ap);
485	}
486	exit(2);
487}
488
489#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) ||	\
490    !defined(NO_XZ_SUPPORT)
491/* ... without an errno. */
492void
493maybe_errx(const char *fmt, ...)
494{
495	va_list ap;
496
497	if (qflag == 0) {
498		va_start(ap, fmt);
499		vwarnx(fmt, ap);
500		va_end(ap);
501	}
502	exit(2);
503}
504#endif
505
506#ifndef SMALL
507/* split up $GZIP and prepend it to the argument list */
508static void
509prepend_gzip(char *gzip, int *argc, char ***argv)
510{
511	char *s, **nargv, **ac;
512	int nenvarg = 0, i;
513
514	/* scan how many arguments there are */
515	for (s = gzip;;) {
516		while (*s == ' ' || *s == '\t')
517			s++;
518		if (*s == 0)
519			goto count_done;
520		nenvarg++;
521		while (*s != ' ' && *s != '\t')
522			if (*s++ == 0)
523				goto count_done;
524	}
525count_done:
526	/* punt early */
527	if (nenvarg == 0)
528		return;
529
530	*argc += nenvarg;
531	ac = *argv;
532
533	nargv = (char **)malloc((*argc + 1) * sizeof(char *));
534	if (nargv == NULL)
535		maybe_err("malloc");
536
537	/* stash this away */
538	*argv = nargv;
539
540	/* copy the program name first */
541	i = 0;
542	nargv[i++] = *(ac++);
543
544	/* take a copy of $GZIP and add it to the array */
545	s = strdup(gzip);
546	if (s == NULL)
547		maybe_err("strdup");
548	for (;;) {
549		/* Skip whitespaces. */
550		while (*s == ' ' || *s == '\t')
551			s++;
552		if (*s == 0)
553			goto copy_done;
554		nargv[i++] = s;
555		/* Find the end of this argument. */
556		while (*s != ' ' && *s != '\t')
557			if (*s++ == 0)
558				/* Argument followed by NUL. */
559				goto copy_done;
560		/* Terminate by overwriting ' ' or '\t' with NUL. */
561		*s++ = 0;
562	}
563copy_done:
564
565	/* copy the original arguments and a NULL */
566	while (*ac)
567		nargv[i++] = *(ac++);
568	nargv[i] = NULL;
569}
570#endif
571
572/* compress input to output. Return bytes read, -1 on error */
573static off_t
574gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
575{
576	z_stream z;
577	char *outbufp, *inbufp;
578	off_t in_tot = 0, out_tot = 0;
579	ssize_t in_size;
580	int i, error;
581	uLong crc;
582#ifdef SMALL
583	static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
584				 0, 0, 0, 0,
585				 0, OS_CODE };
586#endif
587
588	outbufp = malloc(BUFLEN);
589	inbufp = malloc(BUFLEN);
590	if (outbufp == NULL || inbufp == NULL) {
591		maybe_err("malloc failed");
592		goto out;
593	}
594
595	memset(&z, 0, sizeof z);
596	z.zalloc = Z_NULL;
597	z.zfree = Z_NULL;
598	z.opaque = 0;
599
600#ifdef SMALL
601	memcpy(outbufp, header, sizeof header);
602	i = sizeof header;
603#else
604	if (nflag != 0) {
605		mtime = 0;
606		origname = "";
607	}
608
609	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
610		     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
611		     *origname ? ORIG_NAME : 0,
612		     mtime & 0xff,
613		     (mtime >> 8) & 0xff,
614		     (mtime >> 16) & 0xff,
615		     (mtime >> 24) & 0xff,
616		     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
617		     OS_CODE, origname);
618	if (i >= BUFLEN)
619		/* this need PATH_MAX > BUFLEN ... */
620		maybe_err("snprintf");
621	if (*origname)
622		i++;
623#endif
624
625	z.next_out = (unsigned char *)outbufp + i;
626	z.avail_out = BUFLEN - i;
627
628	error = deflateInit2(&z, numflag, Z_DEFLATED,
629			     (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
630	if (error != Z_OK) {
631		maybe_warnx("deflateInit2 failed");
632		in_tot = -1;
633		goto out;
634	}
635
636	crc = crc32(0L, Z_NULL, 0);
637	for (;;) {
638		if (z.avail_out == 0) {
639			if (write_retry(out, outbufp, BUFLEN) != BUFLEN) {
640				maybe_warn("write");
641				out_tot = -1;
642				goto out;
643			}
644
645			out_tot += BUFLEN;
646			z.next_out = (unsigned char *)outbufp;
647			z.avail_out = BUFLEN;
648		}
649
650		if (z.avail_in == 0) {
651			in_size = read(in, inbufp, BUFLEN);
652			if (in_size < 0) {
653				maybe_warn("read");
654				in_tot = -1;
655				goto out;
656			}
657			if (in_size == 0)
658				break;
659			infile_newdata(in_size);
660
661			crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
662			in_tot += in_size;
663			z.next_in = (unsigned char *)inbufp;
664			z.avail_in = in_size;
665		}
666
667		error = deflate(&z, Z_NO_FLUSH);
668		if (error != Z_OK && error != Z_STREAM_END) {
669			maybe_warnx("deflate failed");
670			in_tot = -1;
671			goto out;
672		}
673	}
674
675	/* clean up */
676	for (;;) {
677		size_t len;
678		ssize_t w;
679
680		error = deflate(&z, Z_FINISH);
681		if (error != Z_OK && error != Z_STREAM_END) {
682			maybe_warnx("deflate failed");
683			in_tot = -1;
684			goto out;
685		}
686
687		len = (char *)z.next_out - outbufp;
688
689		w = write_retry(out, outbufp, len);
690		if (w == -1 || (size_t)w != len) {
691			maybe_warn("write");
692			out_tot = -1;
693			goto out;
694		}
695		out_tot += len;
696		z.next_out = (unsigned char *)outbufp;
697		z.avail_out = BUFLEN;
698
699		if (error == Z_STREAM_END)
700			break;
701	}
702
703	if (deflateEnd(&z) != Z_OK) {
704		maybe_warnx("deflateEnd failed");
705		in_tot = -1;
706		goto out;
707	}
708
709	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
710		 (int)crc & 0xff,
711		 (int)(crc >> 8) & 0xff,
712		 (int)(crc >> 16) & 0xff,
713		 (int)(crc >> 24) & 0xff,
714		 (int)in_tot & 0xff,
715		 (int)(in_tot >> 8) & 0xff,
716		 (int)(in_tot >> 16) & 0xff,
717		 (int)(in_tot >> 24) & 0xff);
718	if (i != 8)
719		maybe_err("snprintf");
720	if (write_retry(out, outbufp, i) != i) {
721		maybe_warn("write");
722		in_tot = -1;
723	} else
724		out_tot += i;
725
726out:
727	if (inbufp != NULL)
728		free(inbufp);
729	if (outbufp != NULL)
730		free(outbufp);
731	if (gsizep)
732		*gsizep = out_tot;
733	return in_tot;
734}
735
736/*
737 * uncompress input to output then close the input.  return the
738 * uncompressed size written, and put the compressed sized read
739 * into `*gsizep'.
740 */
741static off_t
742gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
743	      const char *filename)
744{
745	z_stream z;
746	char *outbufp, *inbufp;
747	off_t out_tot = -1, in_tot = 0;
748	uint32_t out_sub_tot = 0;
749	enum {
750		GZSTATE_MAGIC0,
751		GZSTATE_MAGIC1,
752		GZSTATE_METHOD,
753		GZSTATE_FLAGS,
754		GZSTATE_SKIPPING,
755		GZSTATE_EXTRA,
756		GZSTATE_EXTRA2,
757		GZSTATE_EXTRA3,
758		GZSTATE_ORIGNAME,
759		GZSTATE_COMMENT,
760		GZSTATE_HEAD_CRC1,
761		GZSTATE_HEAD_CRC2,
762		GZSTATE_INIT,
763		GZSTATE_READ,
764		GZSTATE_CRC,
765		GZSTATE_LEN,
766	} state = GZSTATE_MAGIC0;
767	int flags = 0, skip_count = 0;
768	int error = Z_STREAM_ERROR, done_reading = 0;
769	uLong crc = 0;
770	ssize_t wr;
771	int needmore = 0;
772
773#define ADVANCE()       { z.next_in++; z.avail_in--; }
774
775	if ((outbufp = malloc(BUFLEN)) == NULL) {
776		maybe_err("malloc failed");
777		goto out2;
778	}
779	if ((inbufp = malloc(BUFLEN)) == NULL) {
780		maybe_err("malloc failed");
781		goto out1;
782	}
783
784	memset(&z, 0, sizeof z);
785	z.avail_in = prelen;
786	z.next_in = (unsigned char *)pre;
787	z.avail_out = BUFLEN;
788	z.next_out = (unsigned char *)outbufp;
789	z.zalloc = NULL;
790	z.zfree = NULL;
791	z.opaque = 0;
792
793	in_tot = prelen;
794	out_tot = 0;
795
796	for (;;) {
797		check_siginfo();
798		if ((z.avail_in == 0 || needmore) && done_reading == 0) {
799			ssize_t in_size;
800
801			if (z.avail_in > 0) {
802				memmove(inbufp, z.next_in, z.avail_in);
803			}
804			z.next_in = (unsigned char *)inbufp;
805			in_size = read(in, z.next_in + z.avail_in,
806			    BUFLEN - z.avail_in);
807
808			if (in_size == -1) {
809				maybe_warn("failed to read stdin");
810				goto stop_and_fail;
811			} else if (in_size == 0) {
812				done_reading = 1;
813			}
814			infile_newdata(in_size);
815
816			z.avail_in += in_size;
817			needmore = 0;
818
819			in_tot += in_size;
820		}
821		if (z.avail_in == 0) {
822			if (done_reading && state != GZSTATE_MAGIC0) {
823				maybe_warnx("%s: unexpected end of file",
824					    filename);
825				goto stop_and_fail;
826			}
827			goto stop;
828		}
829		switch (state) {
830		case GZSTATE_MAGIC0:
831			if (*z.next_in != GZIP_MAGIC0) {
832				if (in_tot > 0) {
833					maybe_warnx("%s: trailing garbage "
834						    "ignored", filename);
835					exit_value = 2;
836					goto stop;
837				}
838				maybe_warnx("input not gziped (MAGIC0)");
839				goto stop_and_fail;
840			}
841			ADVANCE();
842			state++;
843			out_sub_tot = 0;
844			crc = crc32(0L, Z_NULL, 0);
845			break;
846
847		case GZSTATE_MAGIC1:
848			if (*z.next_in != GZIP_MAGIC1 &&
849			    *z.next_in != GZIP_OMAGIC1) {
850				maybe_warnx("input not gziped (MAGIC1)");
851				goto stop_and_fail;
852			}
853			ADVANCE();
854			state++;
855			break;
856
857		case GZSTATE_METHOD:
858			if (*z.next_in != Z_DEFLATED) {
859				maybe_warnx("unknown compression method");
860				goto stop_and_fail;
861			}
862			ADVANCE();
863			state++;
864			break;
865
866		case GZSTATE_FLAGS:
867			flags = *z.next_in;
868			ADVANCE();
869			skip_count = 6;
870			state++;
871			break;
872
873		case GZSTATE_SKIPPING:
874			if (skip_count > 0) {
875				skip_count--;
876				ADVANCE();
877			} else
878				state++;
879			break;
880
881		case GZSTATE_EXTRA:
882			if ((flags & EXTRA_FIELD) == 0) {
883				state = GZSTATE_ORIGNAME;
884				break;
885			}
886			skip_count = *z.next_in;
887			ADVANCE();
888			state++;
889			break;
890
891		case GZSTATE_EXTRA2:
892			skip_count |= ((*z.next_in) << 8);
893			ADVANCE();
894			state++;
895			break;
896
897		case GZSTATE_EXTRA3:
898			if (skip_count > 0) {
899				skip_count--;
900				ADVANCE();
901			} else
902				state++;
903			break;
904
905		case GZSTATE_ORIGNAME:
906			if ((flags & ORIG_NAME) == 0) {
907				state++;
908				break;
909			}
910			if (*z.next_in == 0)
911				state++;
912			ADVANCE();
913			break;
914
915		case GZSTATE_COMMENT:
916			if ((flags & COMMENT) == 0) {
917				state++;
918				break;
919			}
920			if (*z.next_in == 0)
921				state++;
922			ADVANCE();
923			break;
924
925		case GZSTATE_HEAD_CRC1:
926			if (flags & HEAD_CRC)
927				skip_count = 2;
928			else
929				skip_count = 0;
930			state++;
931			break;
932
933		case GZSTATE_HEAD_CRC2:
934			if (skip_count > 0) {
935				skip_count--;
936				ADVANCE();
937			} else
938				state++;
939			break;
940
941		case GZSTATE_INIT:
942			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
943				maybe_warnx("failed to inflateInit");
944				goto stop_and_fail;
945			}
946			state++;
947			break;
948
949		case GZSTATE_READ:
950			error = inflate(&z, Z_FINISH);
951			switch (error) {
952			/* Z_BUF_ERROR goes with Z_FINISH... */
953			case Z_BUF_ERROR:
954				if (z.avail_out > 0 && !done_reading)
955					continue;
956
957			case Z_STREAM_END:
958			case Z_OK:
959				break;
960
961			case Z_NEED_DICT:
962				maybe_warnx("Z_NEED_DICT error");
963				goto stop_and_fail;
964			case Z_DATA_ERROR:
965				maybe_warnx("data stream error");
966				goto stop_and_fail;
967			case Z_STREAM_ERROR:
968				maybe_warnx("internal stream error");
969				goto stop_and_fail;
970			case Z_MEM_ERROR:
971				maybe_warnx("memory allocation error");
972				goto stop_and_fail;
973
974			default:
975				maybe_warn("unknown error from inflate(): %d",
976				    error);
977			}
978			wr = BUFLEN - z.avail_out;
979
980			if (wr != 0) {
981				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
982				if (
983#ifndef SMALL
984				    /* don't write anything with -t */
985				    tflag == 0 &&
986#endif
987				    write_retry(out, outbufp, wr) != wr) {
988					maybe_warn("error writing to output");
989					goto stop_and_fail;
990				}
991
992				out_tot += wr;
993				out_sub_tot += wr;
994			}
995
996			if (error == Z_STREAM_END) {
997				inflateEnd(&z);
998				state++;
999			}
1000
1001			z.next_out = (unsigned char *)outbufp;
1002			z.avail_out = BUFLEN;
1003
1004			break;
1005		case GZSTATE_CRC:
1006			{
1007				uLong origcrc;
1008
1009				if (z.avail_in < 4) {
1010					if (!done_reading) {
1011						needmore = 1;
1012						continue;
1013					}
1014					maybe_warnx("truncated input");
1015					goto stop_and_fail;
1016				}
1017				origcrc = ((unsigned)z.next_in[0] & 0xff) |
1018					((unsigned)z.next_in[1] & 0xff) << 8 |
1019					((unsigned)z.next_in[2] & 0xff) << 16 |
1020					((unsigned)z.next_in[3] & 0xff) << 24;
1021				if (origcrc != crc) {
1022					maybe_warnx("invalid compressed"
1023					     " data--crc error");
1024					goto stop_and_fail;
1025				}
1026			}
1027
1028			z.avail_in -= 4;
1029			z.next_in += 4;
1030
1031			if (!z.avail_in && done_reading) {
1032				goto stop;
1033			}
1034			state++;
1035			break;
1036		case GZSTATE_LEN:
1037			{
1038				uLong origlen;
1039
1040				if (z.avail_in < 4) {
1041					if (!done_reading) {
1042						needmore = 1;
1043						continue;
1044					}
1045					maybe_warnx("truncated input");
1046					goto stop_and_fail;
1047				}
1048				origlen = ((unsigned)z.next_in[0] & 0xff) |
1049					((unsigned)z.next_in[1] & 0xff) << 8 |
1050					((unsigned)z.next_in[2] & 0xff) << 16 |
1051					((unsigned)z.next_in[3] & 0xff) << 24;
1052
1053				if (origlen != out_sub_tot) {
1054					maybe_warnx("invalid compressed"
1055					     " data--length error");
1056					goto stop_and_fail;
1057				}
1058			}
1059
1060			z.avail_in -= 4;
1061			z.next_in += 4;
1062
1063			if (error < 0) {
1064				maybe_warnx("decompression error");
1065				goto stop_and_fail;
1066			}
1067			state = GZSTATE_MAGIC0;
1068			break;
1069		}
1070		continue;
1071stop_and_fail:
1072		out_tot = -1;
1073stop:
1074		break;
1075	}
1076	if (state > GZSTATE_INIT)
1077		inflateEnd(&z);
1078
1079	free(inbufp);
1080out1:
1081	free(outbufp);
1082out2:
1083	if (gsizep)
1084		*gsizep = in_tot;
1085	return (out_tot);
1086}
1087
1088#ifndef SMALL
1089/*
1090 * set the owner, mode, flags & utimes using the given file descriptor.
1091 * file is only used in possible warning messages.
1092 */
1093static void
1094copymodes(int fd, const struct stat *sbp, const char *file)
1095{
1096	struct timespec times[2];
1097	struct stat sb;
1098
1099	/*
1100	 * If we have no info on the input, give this file some
1101	 * default values and return..
1102	 */
1103	if (sbp == NULL) {
1104		mode_t mask = umask(022);
1105
1106		(void)fchmod(fd, DEFFILEMODE & ~mask);
1107		(void)umask(mask);
1108		return;
1109	}
1110	sb = *sbp;
1111
1112	/* if the chown fails, remove set-id bits as-per compress(1) */
1113	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1114		if (errno != EPERM)
1115			maybe_warn("couldn't fchown: %s", file);
1116		sb.st_mode &= ~(S_ISUID|S_ISGID);
1117	}
1118
1119	/* we only allow set-id and the 9 normal permission bits */
1120	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1121	if (fchmod(fd, sb.st_mode) < 0)
1122		maybe_warn("couldn't fchmod: %s", file);
1123
1124	times[0] = sb.st_atim;
1125	times[1] = sb.st_mtim;
1126	if (futimens(fd, times) < 0)
1127		maybe_warn("couldn't futimens: %s", file);
1128
1129	/* only try flags if they exist already */
1130        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1131		maybe_warn("couldn't fchflags: %s", file);
1132}
1133#endif
1134
1135/* what sort of file is this? */
1136static enum filetype
1137file_gettype(u_char *buf)
1138{
1139
1140	if (buf[0] == GZIP_MAGIC0 &&
1141	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1142		return FT_GZIP;
1143	else
1144#ifndef NO_BZIP2_SUPPORT
1145	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1146	    buf[3] >= '0' && buf[3] <= '9')
1147		return FT_BZIP2;
1148	else
1149#endif
1150#ifndef NO_COMPRESS_SUPPORT
1151	if (memcmp(buf, Z_MAGIC, 2) == 0)
1152		return FT_Z;
1153	else
1154#endif
1155#ifndef NO_PACK_SUPPORT
1156	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1157		return FT_PACK;
1158	else
1159#endif
1160#ifndef NO_XZ_SUPPORT
1161	if (memcmp(buf, XZ_MAGIC, 4) == 0)	/* XXX: We only have 4 bytes */
1162		return FT_XZ;
1163	else
1164#endif
1165		return FT_UNKNOWN;
1166}
1167
1168#ifndef SMALL
1169/* check the outfile is OK. */
1170static int
1171check_outfile(const char *outfile)
1172{
1173	struct stat sb;
1174	int ok = 1;
1175
1176	if (lflag == 0 && stat(outfile, &sb) == 0) {
1177		if (fflag)
1178			unlink(outfile);
1179		else if (isatty(STDIN_FILENO)) {
1180			char ans[10] = { 'n', '\0' };	/* default */
1181
1182			fprintf(stderr, "%s already exists -- do you wish to "
1183					"overwrite (y or n)? " , outfile);
1184			(void)fgets(ans, sizeof(ans) - 1, stdin);
1185			if (ans[0] != 'y' && ans[0] != 'Y') {
1186				fprintf(stderr, "\tnot overwriting\n");
1187				ok = 0;
1188			} else
1189				unlink(outfile);
1190		} else {
1191			maybe_warnx("%s already exists -- skipping", outfile);
1192			ok = 0;
1193		}
1194	}
1195	return ok;
1196}
1197
1198static void
1199unlink_input(const char *file, const struct stat *sb)
1200{
1201	struct stat nsb;
1202
1203	if (kflag)
1204		return;
1205	if (stat(file, &nsb) != 0)
1206		/* Must be gone already */
1207		return;
1208	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1209		/* Definitely a different file */
1210		return;
1211	unlink(file);
1212}
1213
1214static void
1215got_sigint(int signo __unused)
1216{
1217
1218	if (remove_file != NULL)
1219		unlink(remove_file);
1220	_exit(2);
1221}
1222
1223static void
1224got_siginfo(int signo __unused)
1225{
1226
1227	print_info = 1;
1228}
1229
1230static void
1231setup_signals(void)
1232{
1233
1234	signal(SIGINFO, got_siginfo);
1235	signal(SIGINT, got_sigint);
1236}
1237
1238static	void
1239infile_newdata(size_t newdata)
1240{
1241
1242	infile_current += newdata;
1243}
1244#endif
1245
1246static	void
1247infile_set(const char *newinfile, off_t total)
1248{
1249
1250	if (newinfile)
1251		infile = newinfile;
1252#ifndef SMALL
1253	infile_total = total;
1254#endif
1255}
1256
1257static	void
1258infile_clear(void)
1259{
1260
1261	infile = NULL;
1262#ifndef SMALL
1263	infile_total = infile_current = 0;
1264#endif
1265}
1266
1267static const suffixes_t *
1268check_suffix(char *file, int xlate)
1269{
1270	const suffixes_t *s;
1271	int len = strlen(file);
1272	char *sp;
1273
1274	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1275		/* if it doesn't fit in "a.suf", don't bother */
1276		if (s->ziplen >= len)
1277			continue;
1278		sp = file + len - s->ziplen;
1279		if (strcmp(s->zipped, sp) != 0)
1280			continue;
1281		if (xlate)
1282			strcpy(sp, s->normal);
1283		return s;
1284	}
1285	return NULL;
1286}
1287
1288/*
1289 * compress the given file: create a corresponding .gz file and remove the
1290 * original.
1291 */
1292static off_t
1293file_compress(char *file, char *outfile, size_t outsize)
1294{
1295	int in;
1296	int out;
1297	off_t size, in_size;
1298#ifndef SMALL
1299	struct stat isb, osb;
1300	const suffixes_t *suff;
1301#endif
1302
1303	in = open(file, O_RDONLY);
1304	if (in == -1) {
1305		maybe_warn("can't open %s", file);
1306		return (-1);
1307	}
1308
1309#ifndef SMALL
1310	if (fstat(in, &isb) != 0) {
1311		maybe_warn("couldn't stat: %s", file);
1312		close(in);
1313		return (-1);
1314	}
1315#endif
1316
1317#ifndef SMALL
1318	if (fstat(in, &isb) != 0) {
1319		close(in);
1320		maybe_warn("can't stat %s", file);
1321		return -1;
1322	}
1323	infile_set(file, isb.st_size);
1324#endif
1325
1326	if (cflag == 0) {
1327#ifndef SMALL
1328		if (isb.st_nlink > 1 && fflag == 0) {
1329			maybe_warnx("%s has %ju other link%s -- "
1330				    "skipping", file,
1331				    (uintmax_t)isb.st_nlink - 1,
1332				    isb.st_nlink == 1 ? "" : "s");
1333			close(in);
1334			return -1;
1335		}
1336
1337		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1338		    suff->zipped[0] != 0) {
1339			maybe_warnx("%s already has %s suffix -- unchanged",
1340			    file, suff->zipped);
1341			close(in);
1342			return (-1);
1343		}
1344#endif
1345
1346		/* Add (usually) .gz to filename */
1347		if ((size_t)snprintf(outfile, outsize, "%s%s",
1348		    file, suffixes[0].zipped) >= outsize)
1349			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1350			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1351
1352#ifndef SMALL
1353		if (check_outfile(outfile) == 0) {
1354			close(in);
1355			return (-1);
1356		}
1357#endif
1358	}
1359
1360	if (cflag == 0) {
1361		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1362		if (out == -1) {
1363			maybe_warn("could not create output: %s", outfile);
1364			fclose(stdin);
1365			return (-1);
1366		}
1367#ifndef SMALL
1368		remove_file = outfile;
1369#endif
1370	} else
1371		out = STDOUT_FILENO;
1372
1373	in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1374
1375	(void)close(in);
1376
1377	/*
1378	 * If there was an error, in_size will be -1.
1379	 * If we compressed to stdout, just return the size.
1380	 * Otherwise stat the file and check it is the correct size.
1381	 * We only blow away the file if we can stat the output and it
1382	 * has the expected size.
1383	 */
1384	if (cflag != 0)
1385		return in_size == -1 ? -1 : size;
1386
1387#ifndef SMALL
1388	if (fstat(out, &osb) != 0) {
1389		maybe_warn("couldn't stat: %s", outfile);
1390		goto bad_outfile;
1391	}
1392
1393	if (osb.st_size != size) {
1394		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1395		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1396		goto bad_outfile;
1397	}
1398
1399	copymodes(out, &isb, outfile);
1400	remove_file = NULL;
1401#endif
1402	if (close(out) == -1)
1403		maybe_warn("couldn't close output");
1404
1405	/* output is good, ok to delete input */
1406	unlink_input(file, &isb);
1407	return (size);
1408
1409#ifndef SMALL
1410    bad_outfile:
1411	if (close(out) == -1)
1412		maybe_warn("couldn't close output");
1413
1414	maybe_warnx("leaving original %s", file);
1415	unlink(outfile);
1416	return (size);
1417#endif
1418}
1419
1420/* uncompress the given file and remove the original */
1421static off_t
1422file_uncompress(char *file, char *outfile, size_t outsize)
1423{
1424	struct stat isb, osb;
1425	off_t size;
1426	ssize_t rbytes;
1427	unsigned char header1[4];
1428	enum filetype method;
1429	int fd, ofd, zfd = -1;
1430	size_t in_size;
1431#ifndef SMALL
1432	ssize_t rv;
1433	time_t timestamp = 0;
1434	char name[PATH_MAX + 1];
1435#endif
1436
1437	/* gather the old name info */
1438
1439	fd = open(file, O_RDONLY);
1440	if (fd < 0) {
1441		maybe_warn("can't open %s", file);
1442		goto lose;
1443	}
1444	if (fstat(fd, &isb) != 0) {
1445		close(fd);
1446		maybe_warn("can't stat %s", file);
1447		goto lose;
1448	}
1449	if (S_ISREG(isb.st_mode))
1450		in_size = isb.st_size;
1451	else
1452		in_size = 0;
1453	infile_set(file, in_size);
1454
1455	strlcpy(outfile, file, outsize);
1456	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1457		maybe_warnx("%s: unknown suffix -- ignored", file);
1458		goto lose;
1459	}
1460
1461	rbytes = read(fd, header1, sizeof header1);
1462	if (rbytes != sizeof header1) {
1463		/* we don't want to fail here. */
1464#ifndef SMALL
1465		if (fflag)
1466			goto lose;
1467#endif
1468		if (rbytes == -1)
1469			maybe_warn("can't read %s", file);
1470		else
1471			goto unexpected_EOF;
1472		goto lose;
1473	}
1474	infile_newdata(rbytes);
1475
1476	method = file_gettype(header1);
1477#ifndef SMALL
1478	if (fflag == 0 && method == FT_UNKNOWN) {
1479		maybe_warnx("%s: not in gzip format", file);
1480		goto lose;
1481	}
1482
1483#endif
1484
1485#ifndef SMALL
1486	if (method == FT_GZIP && Nflag) {
1487		unsigned char ts[4];	/* timestamp */
1488
1489		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1490		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1491			goto unexpected_EOF;
1492		if (rv == -1) {
1493			if (!fflag)
1494				maybe_warn("can't read %s", file);
1495			goto lose;
1496		}
1497		infile_newdata(rv);
1498		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1499
1500		if (header1[3] & ORIG_NAME) {
1501			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
1502			if (rbytes < 0) {
1503				maybe_warn("can't read %s", file);
1504				goto lose;
1505			}
1506			if (name[0] != '\0') {
1507				char *dp, *nf;
1508
1509				/* Make sure that name is NUL-terminated */
1510				name[rbytes] = '\0';
1511
1512				/* strip saved directory name */
1513				nf = strrchr(name, '/');
1514				if (nf == NULL)
1515					nf = name;
1516				else
1517					nf++;
1518
1519				/* preserve original directory name */
1520				dp = strrchr(file, '/');
1521				if (dp == NULL)
1522					dp = file;
1523				else
1524					dp++;
1525				snprintf(outfile, outsize, "%.*s%.*s",
1526						(int) (dp - file),
1527						file, (int) rbytes, nf);
1528			}
1529		}
1530	}
1531#endif
1532	lseek(fd, 0, SEEK_SET);
1533
1534	if (cflag == 0 || lflag) {
1535#ifndef SMALL
1536		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1537			maybe_warnx("%s has %ju other links -- skipping",
1538			    file, (uintmax_t)isb.st_nlink - 1);
1539			goto lose;
1540		}
1541		if (nflag == 0 && timestamp)
1542			isb.st_mtime = timestamp;
1543		if (check_outfile(outfile) == 0)
1544			goto lose;
1545#endif
1546	}
1547
1548	if (cflag)
1549		zfd = STDOUT_FILENO;
1550	else if (lflag)
1551		zfd = -1;
1552	else {
1553		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1554		if (zfd == STDOUT_FILENO) {
1555			/* We won't close STDOUT_FILENO later... */
1556			zfd = dup(zfd);
1557			close(STDOUT_FILENO);
1558		}
1559		if (zfd == -1) {
1560			maybe_warn("can't open %s", outfile);
1561			goto lose;
1562		}
1563		remove_file = outfile;
1564	}
1565
1566	switch (method) {
1567#ifndef NO_BZIP2_SUPPORT
1568	case FT_BZIP2:
1569		/* XXX */
1570		if (lflag) {
1571			maybe_warnx("no -l with bzip2 files");
1572			goto lose;
1573		}
1574
1575		size = unbzip2(fd, zfd, NULL, 0, NULL);
1576		break;
1577#endif
1578
1579#ifndef NO_COMPRESS_SUPPORT
1580	case FT_Z: {
1581		FILE *in, *out;
1582
1583		/* XXX */
1584		if (lflag) {
1585			maybe_warnx("no -l with Lempel-Ziv files");
1586			goto lose;
1587		}
1588
1589		if ((in = zdopen(fd)) == NULL) {
1590			maybe_warn("zdopen for read: %s", file);
1591			goto lose;
1592		}
1593
1594		out = fdopen(dup(zfd), "w");
1595		if (out == NULL) {
1596			maybe_warn("fdopen for write: %s", outfile);
1597			fclose(in);
1598			goto lose;
1599		}
1600
1601		size = zuncompress(in, out, NULL, 0, NULL);
1602		/* need to fclose() if ferror() is true... */
1603		if (ferror(in) | fclose(in)) {
1604			maybe_warn("failed infile fclose");
1605			unlink(outfile);
1606			(void)fclose(out);
1607		}
1608		if (fclose(out) != 0) {
1609			maybe_warn("failed outfile fclose");
1610			unlink(outfile);
1611			goto lose;
1612		}
1613		break;
1614	}
1615#endif
1616
1617#ifndef NO_PACK_SUPPORT
1618	case FT_PACK:
1619		if (lflag) {
1620			maybe_warnx("no -l with packed files");
1621			goto lose;
1622		}
1623
1624		size = unpack(fd, zfd, NULL, 0, NULL);
1625		break;
1626#endif
1627
1628#ifndef NO_XZ_SUPPORT
1629	case FT_XZ:
1630		if (lflag) {
1631			maybe_warnx("no -l with xz files");
1632			goto lose;
1633		}
1634
1635		size = unxz(fd, zfd, NULL, 0, NULL);
1636		break;
1637#endif
1638
1639#ifndef SMALL
1640	case FT_UNKNOWN:
1641		if (lflag) {
1642			maybe_warnx("no -l for unknown filetypes");
1643			goto lose;
1644		}
1645		size = cat_fd(NULL, 0, NULL, fd);
1646		break;
1647#endif
1648	default:
1649		if (lflag) {
1650			print_list(fd, in_size, outfile, isb.st_mtime);
1651			close(fd);
1652			return -1;	/* XXX */
1653		}
1654
1655		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1656		break;
1657	}
1658
1659	if (close(fd) != 0)
1660		maybe_warn("couldn't close input");
1661	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1662		maybe_warn("couldn't close output");
1663
1664	if (size == -1) {
1665		if (cflag == 0)
1666			unlink(outfile);
1667		maybe_warnx("%s: uncompress failed", file);
1668		return -1;
1669	}
1670
1671	/* if testing, or we uncompressed to stdout, this is all we need */
1672#ifndef SMALL
1673	if (tflag)
1674		return size;
1675#endif
1676	/* if we are uncompressing to stdin, don't remove the file. */
1677	if (cflag)
1678		return size;
1679
1680	/*
1681	 * if we create a file...
1682	 */
1683	/*
1684	 * if we can't stat the file don't remove the file.
1685	 */
1686
1687	ofd = open(outfile, O_RDWR, 0);
1688	if (ofd == -1) {
1689		maybe_warn("couldn't open (leaving original): %s",
1690			   outfile);
1691		return -1;
1692	}
1693	if (fstat(ofd, &osb) != 0) {
1694		maybe_warn("couldn't stat (leaving original): %s",
1695			   outfile);
1696		close(ofd);
1697		return -1;
1698	}
1699	if (osb.st_size != size) {
1700		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1701		    (uintmax_t)size, (uintmax_t)osb.st_size);
1702		close(ofd);
1703		unlink(outfile);
1704		return -1;
1705	}
1706#ifndef SMALL
1707	copymodes(ofd, &isb, outfile);
1708	remove_file = NULL;
1709#endif
1710	close(ofd);
1711	unlink_input(file, &isb);
1712	return size;
1713
1714    unexpected_EOF:
1715	maybe_warnx("%s: unexpected end of file", file);
1716    lose:
1717	if (fd != -1)
1718		close(fd);
1719	if (zfd != -1 && zfd != STDOUT_FILENO)
1720		close(zfd);
1721	return -1;
1722}
1723
1724#ifndef SMALL
1725static void
1726check_siginfo(void)
1727{
1728	if (print_info == 0)
1729		return;
1730	if (infile) {
1731		if (infile_total) {
1732			int pcent = (int)((100.0 * infile_current) / infile_total);
1733
1734			fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n",
1735				infile, (unsigned long long)infile_current,
1736				(unsigned long long)infile_total, pcent);
1737		} else
1738			fprintf(stderr, "%s: done %llu bytes\n",
1739				infile, (unsigned long long)infile_current);
1740	}
1741	print_info = 0;
1742}
1743
1744static off_t
1745cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1746{
1747	char buf[BUFLEN];
1748	off_t in_tot;
1749	ssize_t w;
1750
1751	in_tot = count;
1752	w = write_retry(STDOUT_FILENO, prepend, count);
1753	if (w == -1 || (size_t)w != count) {
1754		maybe_warn("write to stdout");
1755		return -1;
1756	}
1757	for (;;) {
1758		ssize_t rv;
1759
1760		rv = read(fd, buf, sizeof buf);
1761		if (rv == 0)
1762			break;
1763		if (rv < 0) {
1764			maybe_warn("read from fd %d", fd);
1765			break;
1766		}
1767		infile_newdata(rv);
1768
1769		if (write_retry(STDOUT_FILENO, buf, rv) != rv) {
1770			maybe_warn("write to stdout");
1771			break;
1772		}
1773		in_tot += rv;
1774	}
1775
1776	if (gsizep)
1777		*gsizep = in_tot;
1778	return (in_tot);
1779}
1780#endif
1781
1782static void
1783handle_stdin(void)
1784{
1785	struct stat isb;
1786	unsigned char header1[4];
1787	size_t in_size;
1788	off_t usize, gsize;
1789	enum filetype method;
1790	ssize_t bytes_read;
1791#ifndef NO_COMPRESS_SUPPORT
1792	FILE *in;
1793#endif
1794
1795#ifndef SMALL
1796	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1797		maybe_warnx("standard input is a terminal -- ignoring");
1798		goto out;
1799	}
1800#endif
1801
1802	if (fstat(STDIN_FILENO, &isb) < 0) {
1803		maybe_warn("fstat");
1804		goto out;
1805	}
1806	if (S_ISREG(isb.st_mode))
1807		in_size = isb.st_size;
1808	else
1809		in_size = 0;
1810	infile_set("(stdin)", in_size);
1811
1812	if (lflag) {
1813		print_list(STDIN_FILENO, in_size, infile, isb.st_mtime);
1814		goto out;
1815	}
1816
1817	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1818	if (bytes_read == -1) {
1819		maybe_warn("can't read stdin");
1820		goto out;
1821	} else if (bytes_read != sizeof(header1)) {
1822		maybe_warnx("(stdin): unexpected end of file");
1823		goto out;
1824	}
1825
1826	method = file_gettype(header1);
1827	switch (method) {
1828	default:
1829#ifndef SMALL
1830		if (fflag == 0) {
1831			maybe_warnx("unknown compression format");
1832			goto out;
1833		}
1834		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1835		break;
1836#endif
1837	case FT_GZIP:
1838		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1839			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1840		break;
1841#ifndef NO_BZIP2_SUPPORT
1842	case FT_BZIP2:
1843		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1844				(char *)header1, sizeof header1, &gsize);
1845		break;
1846#endif
1847#ifndef NO_COMPRESS_SUPPORT
1848	case FT_Z:
1849		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1850			maybe_warnx("zopen of stdin");
1851			goto out;
1852		}
1853
1854		usize = zuncompress(in, stdout, (char *)header1,
1855		    sizeof header1, &gsize);
1856		fclose(in);
1857		break;
1858#endif
1859#ifndef NO_PACK_SUPPORT
1860	case FT_PACK:
1861		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1862			       (char *)header1, sizeof header1, &gsize);
1863		break;
1864#endif
1865#ifndef NO_XZ_SUPPORT
1866	case FT_XZ:
1867		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1868			     (char *)header1, sizeof header1, &gsize);
1869		break;
1870#endif
1871	}
1872
1873#ifndef SMALL
1874        if (vflag && !tflag && usize != -1 && gsize != -1)
1875		print_verbage(NULL, NULL, usize, gsize);
1876	if (vflag && tflag)
1877		print_test("(stdin)", usize != -1);
1878#else
1879	(void)&usize;
1880#endif
1881
1882out:
1883	infile_clear();
1884}
1885
1886static void
1887handle_stdout(void)
1888{
1889	off_t gsize;
1890#ifndef SMALL
1891	off_t usize;
1892	struct stat sb;
1893	time_t systime;
1894	uint32_t mtime;
1895	int ret;
1896
1897	infile_set("(stdout)", 0);
1898
1899	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1900		maybe_warnx("standard output is a terminal -- ignoring");
1901		return;
1902	}
1903
1904	/* If stdin is a file use its mtime, otherwise use current time */
1905	ret = fstat(STDIN_FILENO, &sb);
1906	if (ret < 0) {
1907		maybe_warn("Can't stat stdin");
1908		return;
1909	}
1910
1911	if (S_ISREG(sb.st_mode)) {
1912		infile_set("(stdout)", sb.st_size);
1913		mtime = (uint32_t)sb.st_mtime;
1914	} else {
1915		systime = time(NULL);
1916		if (systime == -1) {
1917			maybe_warn("time");
1918			return;
1919		}
1920		mtime = (uint32_t)systime;
1921	}
1922
1923	usize =
1924#endif
1925		gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1926#ifndef SMALL
1927        if (vflag && !tflag && usize != -1 && gsize != -1)
1928		print_verbage(NULL, NULL, usize, gsize);
1929#endif
1930}
1931
1932/* do what is asked for, for the path name */
1933static void
1934handle_pathname(char *path)
1935{
1936	char *opath = path, *s = NULL;
1937	ssize_t len;
1938	int slen;
1939	struct stat sb;
1940
1941	/* check for stdout/stdin */
1942	if (path[0] == '-' && path[1] == '\0') {
1943		if (dflag)
1944			handle_stdin();
1945		else
1946			handle_stdout();
1947		return;
1948	}
1949
1950retry:
1951	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1952	    lstat(path, &sb) != 0)) {
1953		/* lets try <path>.gz if we're decompressing */
1954		if (dflag && s == NULL && errno == ENOENT) {
1955			len = strlen(path);
1956			slen = suffixes[0].ziplen;
1957			s = malloc(len + slen + 1);
1958			if (s == NULL)
1959				maybe_err("malloc");
1960			memcpy(s, path, len);
1961			memcpy(s + len, suffixes[0].zipped, slen + 1);
1962			path = s;
1963			goto retry;
1964		}
1965		maybe_warn("can't stat: %s", opath);
1966		goto out;
1967	}
1968
1969	if (S_ISDIR(sb.st_mode)) {
1970#ifndef SMALL
1971		if (rflag)
1972			handle_dir(path);
1973		else
1974#endif
1975			maybe_warnx("%s is a directory", path);
1976		goto out;
1977	}
1978
1979	if (S_ISREG(sb.st_mode))
1980		handle_file(path, &sb);
1981	else
1982		maybe_warnx("%s is not a regular file", path);
1983
1984out:
1985	if (s)
1986		free(s);
1987}
1988
1989/* compress/decompress a file */
1990static void
1991handle_file(char *file, struct stat *sbp)
1992{
1993	off_t usize, gsize;
1994	char	outfile[PATH_MAX];
1995
1996	infile_set(file, sbp->st_size);
1997	if (dflag) {
1998		usize = file_uncompress(file, outfile, sizeof(outfile));
1999#ifndef SMALL
2000		if (vflag && tflag)
2001			print_test(file, usize != -1);
2002#endif
2003		if (usize == -1)
2004			return;
2005		gsize = sbp->st_size;
2006	} else {
2007		gsize = file_compress(file, outfile, sizeof(outfile));
2008		if (gsize == -1)
2009			return;
2010		usize = sbp->st_size;
2011	}
2012	infile_clear();
2013
2014#ifndef SMALL
2015	if (vflag && !tflag)
2016		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
2017#endif
2018}
2019
2020#ifndef SMALL
2021/* this is used with -r to recursively descend directories */
2022static void
2023handle_dir(char *dir)
2024{
2025	char *path_argv[2];
2026	FTS *fts;
2027	FTSENT *entry;
2028
2029	path_argv[0] = dir;
2030	path_argv[1] = 0;
2031	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
2032	if (fts == NULL) {
2033		warn("couldn't fts_open %s", dir);
2034		return;
2035	}
2036
2037	while ((entry = fts_read(fts))) {
2038		switch(entry->fts_info) {
2039		case FTS_D:
2040		case FTS_DP:
2041			continue;
2042
2043		case FTS_DNR:
2044		case FTS_ERR:
2045		case FTS_NS:
2046			maybe_warn("%s", entry->fts_path);
2047			continue;
2048		case FTS_F:
2049			handle_file(entry->fts_path, entry->fts_statp);
2050		}
2051	}
2052	(void)fts_close(fts);
2053}
2054#endif
2055
2056/* print a ratio - size reduction as a fraction of uncompressed size */
2057static void
2058print_ratio(off_t in, off_t out, FILE *where)
2059{
2060	int percent10;	/* 10 * percent */
2061	off_t diff;
2062	char buff[8];
2063	int len;
2064
2065	diff = in - out/2;
2066	if (in == 0 && out == 0)
2067		percent10 = 0;
2068	else if (diff < 0)
2069		/*
2070		 * Output is more than double size of input! print -99.9%
2071		 * Quite possibly we've failed to get the original size.
2072		 */
2073		percent10 = -999;
2074	else {
2075		/*
2076		 * We only need 12 bits of result from the final division,
2077		 * so reduce the values until a 32bit division will suffice.
2078		 */
2079		while (in > 0x100000) {
2080			diff >>= 1;
2081			in >>= 1;
2082		}
2083		if (in != 0)
2084			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
2085		else
2086			percent10 = 0;
2087	}
2088
2089	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
2090	/* Move the '.' to before the last digit */
2091	buff[len - 1] = buff[len - 2];
2092	buff[len - 2] = '.';
2093	fprintf(where, "%5s%%", buff);
2094}
2095
2096#ifndef SMALL
2097/* print compression statistics, and the new name (if there is one!) */
2098static void
2099print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
2100{
2101	if (file)
2102		fprintf(stderr, "%s:%s  ", file,
2103		    strlen(file) < 7 ? "\t\t" : "\t");
2104	print_ratio(usize, gsize, stderr);
2105	if (nfile)
2106		fprintf(stderr, " -- replaced with %s", nfile);
2107	fprintf(stderr, "\n");
2108	fflush(stderr);
2109}
2110
2111/* print test results */
2112static void
2113print_test(const char *file, int ok)
2114{
2115
2116	if (exit_value == 0 && ok == 0)
2117		exit_value = 1;
2118	fprintf(stderr, "%s:%s  %s\n", file,
2119	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
2120	fflush(stderr);
2121}
2122#endif
2123
2124/* print a file's info ala --list */
2125/* eg:
2126  compressed uncompressed  ratio uncompressed_name
2127      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
2128*/
2129static void
2130print_list(int fd, off_t out, const char *outfile, time_t ts)
2131{
2132	static int first = 1;
2133#ifndef SMALL
2134	static off_t in_tot, out_tot;
2135	uint32_t crc = 0;
2136#endif
2137	off_t in = 0, rv;
2138
2139	if (first) {
2140#ifndef SMALL
2141		if (vflag)
2142			printf("method  crc     date  time  ");
2143#endif
2144		if (qflag == 0)
2145			printf("  compressed uncompressed  "
2146			       "ratio uncompressed_name\n");
2147	}
2148	first = 0;
2149
2150	/* print totals? */
2151#ifndef SMALL
2152	if (fd == -1) {
2153		in = in_tot;
2154		out = out_tot;
2155	} else
2156#endif
2157	{
2158		/* read the last 4 bytes - this is the uncompressed size */
2159		rv = lseek(fd, (off_t)(-8), SEEK_END);
2160		if (rv != -1) {
2161			unsigned char buf[8];
2162			uint32_t usize;
2163
2164			rv = read(fd, (char *)buf, sizeof(buf));
2165			if (rv == -1)
2166				maybe_warn("read of uncompressed size");
2167			else if (rv != sizeof(buf))
2168				maybe_warnx("read of uncompressed size");
2169
2170			else {
2171				usize = buf[4] | buf[5] << 8 |
2172					buf[6] << 16 | buf[7] << 24;
2173				in = (off_t)usize;
2174#ifndef SMALL
2175				crc = buf[0] | buf[1] << 8 |
2176				      buf[2] << 16 | buf[3] << 24;
2177#endif
2178			}
2179		}
2180	}
2181
2182#ifndef SMALL
2183	if (vflag && fd == -1)
2184		printf("                            ");
2185	else if (vflag) {
2186		char *date = ctime(&ts);
2187
2188		/* skip the day, 1/100th second, and year */
2189		date += 4;
2190		date[12] = 0;
2191		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2192	}
2193	in_tot += in;
2194	out_tot += out;
2195#else
2196	(void)&ts;	/* XXX */
2197#endif
2198	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2199	print_ratio(in, out, stdout);
2200	printf(" %s\n", outfile);
2201}
2202
2203/* display the usage of NetBSD gzip */
2204static void
2205usage(void)
2206{
2207
2208	fprintf(stderr, "%s\n", gzip_version);
2209	fprintf(stderr,
2210#ifdef SMALL
2211    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2212#else
2213    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2214    " -1 --fast            fastest (worst) compression\n"
2215    " -2 .. -8             set compression level\n"
2216    " -9 --best            best (slowest) compression\n"
2217    " -c --stdout          write to stdout, keep original files\n"
2218    "    --to-stdout\n"
2219    " -d --decompress      uncompress files\n"
2220    "    --uncompress\n"
2221    " -f --force           force overwriting & compress links\n"
2222    " -h --help            display this help\n"
2223    " -k --keep            don't delete input files during operation\n"
2224    " -l --list            list compressed file contents\n"
2225    " -N --name            save or restore original file name and time stamp\n"
2226    " -n --no-name         don't save original file name or time stamp\n"
2227    " -q --quiet           output no warnings\n"
2228    " -r --recursive       recursively compress files in directories\n"
2229    " -S .suf              use suffix .suf instead of .gz\n"
2230    "    --suffix .suf\n"
2231    " -t --test            test compressed file\n"
2232    " -V --version         display program version\n"
2233    " -v --verbose         print extra statistics\n",
2234#endif
2235	    getprogname());
2236	exit(0);
2237}
2238
2239#ifndef SMALL
2240/* display the license information of FreeBSD gzip */
2241static void
2242display_license(void)
2243{
2244
2245	fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version);
2246	fprintf(stderr, "%s\n", gzip_copyright);
2247	exit(0);
2248}
2249#endif
2250
2251/* display the version of NetBSD gzip */
2252static void
2253display_version(void)
2254{
2255
2256	fprintf(stderr, "%s\n", gzip_version);
2257	exit(0);
2258}
2259
2260#ifndef NO_BZIP2_SUPPORT
2261#include "unbzip2.c"
2262#endif
2263#ifndef NO_COMPRESS_SUPPORT
2264#include "zuncompress.c"
2265#endif
2266#ifndef NO_PACK_SUPPORT
2267#include "unpack.c"
2268#endif
2269#ifndef NO_XZ_SUPPORT
2270#include "unxz.c"
2271#endif
2272
2273static ssize_t
2274read_retry(int fd, void *buf, size_t sz)
2275{
2276	char *cp = buf;
2277	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2278
2279	while (left > 0) {
2280		ssize_t ret;
2281
2282		ret = read(fd, cp, left);
2283		if (ret == -1) {
2284			return ret;
2285		} else if (ret == 0) {
2286			break; /* EOF */
2287		}
2288		cp += ret;
2289		left -= ret;
2290	}
2291
2292	return sz - left;
2293}
2294
2295static ssize_t
2296write_retry(int fd, const void *buf, size_t sz)
2297{
2298	const char *cp = buf;
2299	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2300
2301	while (left > 0) {
2302		ssize_t ret;
2303
2304		ret = write(fd, cp, left);
2305		if (ret == -1) {
2306			return ret;
2307		} else if (ret == 0) {
2308			abort();	/* Can't happen */
2309		}
2310		cp += ret;
2311		left -= ret;
2312	}
2313
2314	return sz - left;
2315}
2316