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