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