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