gzip.c revision 194508
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 194508 2009-06-19 19:28:21Z 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, ...) __dead2
199    __attribute__((__format__(__printf__, 1, 2)));
200#ifndef NO_BZIP2_SUPPORT
201static	void	maybe_errx(const char *fmt, ...) __dead2
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 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		int rv;
1348
1349		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1350		if (rv >= 0 && (size_t)rv < sizeof ts)
1351			goto unexpected_EOF;
1352		if (rv == -1) {
1353			if (!fflag)
1354				maybe_warn("can't read %s", file);
1355			goto lose;
1356		}
1357		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1358
1359		if (header1[3] & ORIG_NAME) {
1360			rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1361			if (rbytes < 0) {
1362				maybe_warn("can't read %s", file);
1363				goto lose;
1364			}
1365			if (name[0] != 0) {
1366				/* preserve original directory name */
1367				char *dp = strrchr(file, '/');
1368				if (dp == NULL)
1369					dp = file;
1370				else
1371					dp++;
1372				snprintf(outfile, outsize, "%.*s%.*s",
1373						(int) (dp - file),
1374						file, (int) rbytes, name);
1375			}
1376		}
1377	}
1378#endif
1379	lseek(fd, 0, SEEK_SET);
1380
1381	if (cflag == 0 || lflag) {
1382		if (fstat(fd, &isb) != 0)
1383			goto lose;
1384#ifndef SMALL
1385		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1386			maybe_warnx("%s has %d other links -- skipping",
1387			    file, isb.st_nlink - 1);
1388			goto lose;
1389		}
1390		if (nflag == 0 && timestamp)
1391			isb.st_mtime = timestamp;
1392		if (check_outfile(outfile) == 0)
1393			goto lose;
1394#endif
1395	}
1396
1397	if (cflag == 0 && lflag == 0) {
1398		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1399		if (zfd == STDOUT_FILENO) {
1400			/* We won't close STDOUT_FILENO later... */
1401			zfd = dup(zfd);
1402			close(STDOUT_FILENO);
1403		}
1404		if (zfd == -1) {
1405			maybe_warn("can't open %s", outfile);
1406			goto lose;
1407		}
1408	} else
1409		zfd = STDOUT_FILENO;
1410
1411#ifndef NO_BZIP2_SUPPORT
1412	if (method == FT_BZIP2) {
1413
1414		/* XXX */
1415		if (lflag) {
1416			maybe_warnx("no -l with bzip2 files");
1417			goto lose;
1418		}
1419
1420		size = unbzip2(fd, zfd, NULL, 0, NULL);
1421	} else
1422#endif
1423
1424#ifndef NO_COMPRESS_SUPPORT
1425	if (method == FT_Z) {
1426		FILE *in, *out;
1427
1428		/* XXX */
1429		if (lflag) {
1430			maybe_warnx("no -l with Lempel-Ziv files");
1431			goto lose;
1432		}
1433
1434		if ((in = zdopen(fd)) == NULL) {
1435			maybe_warn("zdopen for read: %s", file);
1436			goto lose;
1437		}
1438
1439		out = fdopen(dup(zfd), "w");
1440		if (out == NULL) {
1441			maybe_warn("fdopen for write: %s", outfile);
1442			fclose(in);
1443			goto lose;
1444		}
1445
1446		size = zuncompress(in, out, NULL, 0, NULL);
1447		/* need to fclose() if ferror() is true... */
1448		if (ferror(in) | fclose(in)) {
1449			maybe_warn("failed infile fclose");
1450			unlink(outfile);
1451			(void)fclose(out);
1452		}
1453		if (fclose(out) != 0) {
1454			maybe_warn("failed outfile fclose");
1455			unlink(outfile);
1456			goto lose;
1457		}
1458	} else
1459#endif
1460
1461#ifndef SMALL
1462	if (method == FT_UNKNOWN) {
1463		if (lflag) {
1464			maybe_warnx("no -l for unknown filetypes");
1465			goto lose;
1466		}
1467		size = cat_fd(NULL, 0, NULL, fd);
1468	} else
1469#endif
1470	{
1471		if (lflag) {
1472			print_list(fd, isb.st_size, outfile, isb.st_mtime);
1473			close(fd);
1474			return -1;	/* XXX */
1475		}
1476
1477		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1478	}
1479
1480	if (close(fd) != 0)
1481		maybe_warn("couldn't close input");
1482	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1483		maybe_warn("couldn't close output");
1484
1485	if (size == -1) {
1486		if (cflag == 0)
1487			unlink(outfile);
1488		maybe_warnx("%s: uncompress failed", file);
1489		return -1;
1490	}
1491
1492	/* if testing, or we uncompressed to stdout, this is all we need */
1493#ifndef SMALL
1494	if (tflag)
1495		return size;
1496#endif
1497	/* if we are uncompressing to stdin, don't remove the file. */
1498	if (cflag)
1499		return size;
1500
1501	/*
1502	 * if we create a file...
1503	 */
1504	/*
1505	 * if we can't stat the file don't remove the file.
1506	 */
1507
1508	ofd = open(outfile, O_RDWR, 0);
1509	if (ofd == -1) {
1510		maybe_warn("couldn't open (leaving original): %s",
1511			   outfile);
1512		return -1;
1513	}
1514	if (fstat(ofd, &osb) != 0) {
1515		maybe_warn("couldn't stat (leaving original): %s",
1516			   outfile);
1517		close(ofd);
1518		return -1;
1519	}
1520	if (osb.st_size != size) {
1521		maybe_warnx("stat gave different size: %" PRIdOFF
1522				" != %" PRIdOFF " (leaving original)",
1523				size, osb.st_size);
1524		close(ofd);
1525		unlink(outfile);
1526		return -1;
1527	}
1528	unlink_input(file, &isb);
1529#ifndef SMALL
1530	copymodes(ofd, &isb, outfile);
1531#endif
1532	close(ofd);
1533	return size;
1534
1535    unexpected_EOF:
1536	maybe_warnx("%s: unexpected end of file", file);
1537    lose:
1538	if (fd != -1)
1539		close(fd);
1540	if (zfd != -1 && zfd != STDOUT_FILENO)
1541		close(fd);
1542	return -1;
1543}
1544
1545#ifndef SMALL
1546static off_t
1547cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1548{
1549	char buf[BUFLEN];
1550	off_t in_tot;
1551	ssize_t w;
1552
1553	in_tot = count;
1554	w = write(STDOUT_FILENO, prepend, count);
1555	if (w == -1 || (size_t)w != count) {
1556		maybe_warn("write to stdout");
1557		return -1;
1558	}
1559	for (;;) {
1560		ssize_t rv;
1561
1562		rv = read(fd, buf, sizeof buf);
1563		if (rv == 0)
1564			break;
1565		if (rv < 0) {
1566			maybe_warn("read from fd %d", fd);
1567			break;
1568		}
1569
1570		if (write(STDOUT_FILENO, buf, rv) != rv) {
1571			maybe_warn("write to stdout");
1572			break;
1573		}
1574		in_tot += rv;
1575	}
1576
1577	if (gsizep)
1578		*gsizep = in_tot;
1579	return (in_tot);
1580}
1581#endif
1582
1583static void
1584handle_stdin(void)
1585{
1586	unsigned char header1[4];
1587	off_t usize, gsize;
1588	enum filetype method;
1589	ssize_t bytes_read;
1590#ifndef NO_COMPRESS_SUPPORT
1591	FILE *in;
1592#endif
1593
1594#ifndef SMALL
1595	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1596		maybe_warnx("standard input is a terminal -- ignoring");
1597		return;
1598	}
1599#endif
1600
1601	if (lflag) {
1602		struct stat isb;
1603
1604		/* XXX could read the whole file, etc. */
1605		if (fstat(STDIN_FILENO, &isb) < 0) {
1606			maybe_warn("fstat");
1607			return;
1608		}
1609		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1610		return;
1611	}
1612
1613	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1614	if (bytes_read == -1) {
1615		maybe_warn("can't read stdin");
1616		return;
1617	} else if (bytes_read != sizeof(header1)) {
1618		maybe_warnx("(stdin): unexpected end of file");
1619		return;
1620	}
1621
1622	method = file_gettype(header1);
1623	switch (method) {
1624	default:
1625#ifndef SMALL
1626		if (fflag == 0) {
1627			maybe_warnx("unknown compression format");
1628			return;
1629		}
1630		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1631		break;
1632#endif
1633	case FT_GZIP:
1634		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1635			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1636		break;
1637#ifndef NO_BZIP2_SUPPORT
1638	case FT_BZIP2:
1639		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1640				(char *)header1, sizeof header1, &gsize);
1641		break;
1642#endif
1643#ifndef NO_COMPRESS_SUPPORT
1644	case FT_Z:
1645		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1646			maybe_warnx("zopen of stdin");
1647			return;
1648		}
1649
1650		usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize);
1651		fclose(in);
1652		break;
1653#endif
1654	}
1655
1656#ifndef SMALL
1657        if (vflag && !tflag && usize != -1 && gsize != -1)
1658		print_verbage(NULL, NULL, usize, gsize);
1659	if (vflag && tflag)
1660		print_test("(stdin)", usize != -1);
1661#endif
1662
1663}
1664
1665static void
1666handle_stdout(void)
1667{
1668	off_t gsize, usize;
1669	struct stat sb;
1670	time_t systime;
1671	uint32_t mtime;
1672	int ret;
1673
1674#ifndef SMALL
1675	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1676		maybe_warnx("standard output is a terminal -- ignoring");
1677		return;
1678	}
1679#endif
1680	/* If stdin is a file use it's mtime, otherwise use current time */
1681	ret = fstat(STDIN_FILENO, &sb);
1682
1683#ifndef SMALL
1684	if (ret < 0) {
1685		maybe_warn("Can't stat stdin");
1686		return;
1687	}
1688#endif
1689
1690	if (S_ISREG(sb.st_mode))
1691		mtime = (uint32_t)sb.st_mtime;
1692	else {
1693		systime = time(NULL);
1694#ifndef SMALL
1695		if (systime == -1) {
1696			maybe_warn("time");
1697			return;
1698		}
1699#endif
1700		mtime = (uint32_t)systime;
1701	}
1702
1703	usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1704#ifndef SMALL
1705        if (vflag && !tflag && usize != -1 && gsize != -1)
1706		print_verbage(NULL, NULL, usize, gsize);
1707#endif
1708}
1709
1710/* do what is asked for, for the path name */
1711static void
1712handle_pathname(char *path)
1713{
1714	char *opath = path, *s = NULL;
1715	ssize_t len;
1716	int slen;
1717	struct stat sb;
1718
1719	/* check for stdout/stdin */
1720	if (path[0] == '-' && path[1] == '\0') {
1721		if (dflag)
1722			handle_stdin();
1723		else
1724			handle_stdout();
1725		return;
1726	}
1727
1728retry:
1729	if (stat(path, &sb) != 0) {
1730		/* lets try <path>.gz if we're decompressing */
1731		if (dflag && s == NULL && errno == ENOENT) {
1732			len = strlen(path);
1733			slen = suffixes[0].ziplen;
1734			s = malloc(len + slen + 1);
1735			if (s == NULL)
1736				maybe_err("malloc");
1737			memcpy(s, path, len);
1738			memcpy(s + len, suffixes[0].zipped, slen + 1);
1739			path = s;
1740			goto retry;
1741		}
1742		maybe_warn("can't stat: %s", opath);
1743		goto out;
1744	}
1745
1746	if (S_ISDIR(sb.st_mode)) {
1747#ifndef SMALL
1748		if (rflag)
1749			handle_dir(path);
1750		else
1751#endif
1752			maybe_warnx("%s is a directory", path);
1753		goto out;
1754	}
1755
1756	if (S_ISREG(sb.st_mode))
1757		handle_file(path, &sb);
1758	else
1759		maybe_warnx("%s is not a regular file", path);
1760
1761out:
1762	if (s)
1763		free(s);
1764}
1765
1766/* compress/decompress a file */
1767static void
1768handle_file(char *file, struct stat *sbp)
1769{
1770	off_t usize, gsize;
1771	char	outfile[PATH_MAX];
1772
1773	infile = file;
1774	if (dflag) {
1775		usize = file_uncompress(file, outfile, sizeof(outfile));
1776#ifndef SMALL
1777		if (vflag && tflag)
1778			print_test(file, usize != -1);
1779#endif
1780		if (usize == -1)
1781			return;
1782		gsize = sbp->st_size;
1783	} else {
1784		gsize = file_compress(file, outfile, sizeof(outfile));
1785		if (gsize == -1)
1786			return;
1787		usize = sbp->st_size;
1788	}
1789
1790
1791#ifndef SMALL
1792	if (vflag && !tflag)
1793		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1794#endif
1795}
1796
1797#ifndef SMALL
1798/* this is used with -r to recursively descend directories */
1799static void
1800handle_dir(char *dir)
1801{
1802	char *path_argv[2];
1803	FTS *fts;
1804	FTSENT *entry;
1805
1806	path_argv[0] = dir;
1807	path_argv[1] = 0;
1808	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1809	if (fts == NULL) {
1810		warn("couldn't fts_open %s", dir);
1811		return;
1812	}
1813
1814	while ((entry = fts_read(fts))) {
1815		switch(entry->fts_info) {
1816		case FTS_D:
1817		case FTS_DP:
1818			continue;
1819
1820		case FTS_DNR:
1821		case FTS_ERR:
1822		case FTS_NS:
1823			maybe_warn("%s", entry->fts_path);
1824			continue;
1825		case FTS_F:
1826			handle_file(entry->fts_path, entry->fts_statp);
1827		}
1828	}
1829	(void)fts_close(fts);
1830}
1831#endif
1832
1833/* print a ratio - size reduction as a fraction of uncompressed size */
1834static void
1835print_ratio(off_t in, off_t out, FILE *where)
1836{
1837	int percent10;	/* 10 * percent */
1838	off_t diff;
1839	char buff[8];
1840	int len;
1841
1842	diff = in - out/2;
1843	if (diff <= 0)
1844		/*
1845		 * Output is more than double size of input! print -99.9%
1846		 * Quite possibly we've failed to get the original size.
1847		 */
1848		percent10 = -999;
1849	else {
1850		/*
1851		 * We only need 12 bits of result from the final division,
1852		 * so reduce the values until a 32bit division will suffice.
1853		 */
1854		while (in > 0x100000) {
1855			diff >>= 1;
1856			in >>= 1;
1857		}
1858		if (in != 0)
1859			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1860		else
1861			percent10 = 0;
1862	}
1863
1864	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1865	/* Move the '.' to before the last digit */
1866	buff[len - 1] = buff[len - 2];
1867	buff[len - 2] = '.';
1868	fprintf(where, "%5s%%", buff);
1869}
1870
1871#ifndef SMALL
1872/* print compression statistics, and the new name (if there is one!) */
1873static void
1874print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1875{
1876	if (file)
1877		fprintf(stderr, "%s:%s  ", file,
1878		    strlen(file) < 7 ? "\t\t" : "\t");
1879	print_ratio(usize, gsize, stderr);
1880	if (nfile)
1881		fprintf(stderr, " -- replaced with %s", nfile);
1882	fprintf(stderr, "\n");
1883	fflush(stderr);
1884}
1885
1886/* print test results */
1887static void
1888print_test(const char *file, int ok)
1889{
1890
1891	if (exit_value == 0 && ok == 0)
1892		exit_value = 1;
1893	fprintf(stderr, "%s:%s  %s\n", file,
1894	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1895	fflush(stderr);
1896}
1897#endif
1898
1899/* print a file's info ala --list */
1900/* eg:
1901  compressed uncompressed  ratio uncompressed_name
1902      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1903*/
1904static void
1905print_list(int fd, off_t out, const char *outfile, time_t ts)
1906{
1907	static int first = 1;
1908#ifndef SMALL
1909	static off_t in_tot, out_tot;
1910	uint32_t crc = 0;
1911#endif
1912	off_t in = 0, rv;
1913
1914	if (first) {
1915#ifndef SMALL
1916		if (vflag)
1917			printf("method  crc     date  time  ");
1918#endif
1919		if (qflag == 0)
1920			printf("  compressed uncompressed  "
1921			       "ratio uncompressed_name\n");
1922	}
1923	first = 0;
1924
1925	/* print totals? */
1926#ifndef SMALL
1927	if (fd == -1) {
1928		in = in_tot;
1929		out = out_tot;
1930	} else
1931#endif
1932	{
1933		/* read the last 4 bytes - this is the uncompressed size */
1934		rv = lseek(fd, (off_t)(-8), SEEK_END);
1935		if (rv != -1) {
1936			unsigned char buf[8];
1937			uint32_t usize;
1938
1939			rv = read(fd, (char *)buf, sizeof(buf));
1940			if (rv == -1)
1941				maybe_warn("read of uncompressed size");
1942			else if (rv != sizeof(buf))
1943				maybe_warnx("read of uncompressed size");
1944
1945			else {
1946				usize = buf[4] | buf[5] << 8 |
1947					buf[6] << 16 | buf[7] << 24;
1948				in = (off_t)usize;
1949#ifndef SMALL
1950				crc = buf[0] | buf[1] << 8 |
1951				      buf[2] << 16 | buf[3] << 24;
1952#endif
1953			}
1954		}
1955	}
1956
1957#ifndef SMALL
1958	if (vflag && fd == -1)
1959		printf("                            ");
1960	else if (vflag) {
1961		char *date = ctime(&ts);
1962
1963		/* skip the day, 1/100th second, and year */
1964		date += 4;
1965		date[12] = 0;
1966		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
1967	}
1968	in_tot += in;
1969	out_tot += out;
1970#else
1971	(void)&ts;	/* XXX */
1972#endif
1973	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
1974	print_ratio(in, out, stdout);
1975	printf(" %s\n", outfile);
1976}
1977
1978/* display the usage of NetBSD gzip */
1979static void
1980usage(void)
1981{
1982
1983	fprintf(stderr, "%s\n", gzip_version);
1984	fprintf(stderr,
1985#ifdef SMALL
1986    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
1987#else
1988    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
1989    " -1 --fast            fastest (worst) compression\n"
1990    " -2 .. -8             set compression level\n"
1991    " -9 --best            best (slowest) compression\n"
1992    " -c --stdout          write to stdout, keep original files\n"
1993    "    --to-stdout\n"
1994    " -d --decompress      uncompress files\n"
1995    "    --uncompress\n"
1996    " -f --force           force overwriting & compress links\n"
1997    " -h --help            display this help\n"
1998    " -k --keep            don't delete input files during operation\n"
1999    " -l --list            list compressed file contents\n"
2000    " -N --name            save or restore original file name and time stamp\n"
2001    " -n --no-name         don't save original file name or time stamp\n"
2002    " -q --quiet           output no warnings\n"
2003    " -r --recursive       recursively compress files in directories\n"
2004    " -S .suf              use suffix .suf instead of .gz\n"
2005    "    --suffix .suf\n"
2006    " -t --test            test compressed file\n"
2007    " -V --version         display program version\n"
2008    " -v --verbose         print extra statistics\n",
2009#endif
2010	    getprogname());
2011	exit(0);
2012}
2013
2014#ifndef SMALL
2015/* display the license information of FreeBSD gzip */
2016static void
2017display_license(void)
2018{
2019
2020	fprintf(stderr, "%s (based on NetBSD gzip 20060927)\n", gzip_version);
2021	fprintf(stderr, "%s\n", gzip_copyright);
2022	exit(0);
2023}
2024#endif
2025
2026/* display the version of NetBSD gzip */
2027static void
2028display_version(void)
2029{
2030
2031	fprintf(stderr, "%s\n", gzip_version);
2032	exit(0);
2033}
2034
2035#ifndef NO_BZIP2_SUPPORT
2036#include "unbzip2.c"
2037#endif
2038#ifndef NO_COMPRESS_SUPPORT
2039#include "zuncompress.c"
2040#endif
2041
2042static ssize_t
2043read_retry(int fd, void *buf, size_t sz)
2044{
2045	char *cp = buf;
2046	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2047
2048	while (left > 0) {
2049		ssize_t ret;
2050
2051		ret = read(fd, cp, left);
2052		if (ret == -1) {
2053			return ret;
2054		} else if (ret == 0) {
2055			break; /* EOF */
2056		}
2057		cp += ret;
2058		left -= ret;
2059	}
2060
2061	return sz - left;
2062}
2063
2064