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