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