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