gzip.c revision 222210
1/*	$NetBSD: gzip.c,v 1.99 2011/03/23 12:59:44 tsutsui 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__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 222210 2011-05-23 09:02:44Z 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 20110523";
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 "123456789cdhlV"
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				if (z.avail_out > 0 && !done_reading)
920					continue;
921
922			case Z_STREAM_END:
923			case Z_OK:
924				break;
925
926			case Z_NEED_DICT:
927				maybe_warnx("Z_NEED_DICT error");
928				goto stop_and_fail;
929			case Z_DATA_ERROR:
930				maybe_warnx("data stream error");
931				goto stop_and_fail;
932			case Z_STREAM_ERROR:
933				maybe_warnx("internal stream error");
934				goto stop_and_fail;
935			case Z_MEM_ERROR:
936				maybe_warnx("memory allocation error");
937				goto stop_and_fail;
938
939			default:
940				maybe_warn("unknown error from inflate(): %d",
941				    error);
942			}
943			wr = BUFLEN - z.avail_out;
944
945			if (wr != 0) {
946				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
947				if (
948#ifndef SMALL
949				    /* don't write anything with -t */
950				    tflag == 0 &&
951#endif
952				    write(out, outbufp, wr) != wr) {
953					maybe_warn("error writing to output");
954					goto stop_and_fail;
955				}
956
957				out_tot += wr;
958				out_sub_tot += wr;
959			}
960
961			if (error == Z_STREAM_END) {
962				inflateEnd(&z);
963				state++;
964			}
965
966			z.next_out = (unsigned char *)outbufp;
967			z.avail_out = BUFLEN;
968
969			break;
970		case GZSTATE_CRC:
971			{
972				uLong origcrc;
973
974				if (z.avail_in < 4) {
975					if (!done_reading) {
976						needmore = 1;
977						continue;
978					}
979					maybe_warnx("truncated input");
980					goto stop_and_fail;
981				}
982				origcrc = ((unsigned)z.next_in[0] & 0xff) |
983					((unsigned)z.next_in[1] & 0xff) << 8 |
984					((unsigned)z.next_in[2] & 0xff) << 16 |
985					((unsigned)z.next_in[3] & 0xff) << 24;
986				if (origcrc != crc) {
987					maybe_warnx("invalid compressed"
988					     " data--crc error");
989					goto stop_and_fail;
990				}
991			}
992
993			z.avail_in -= 4;
994			z.next_in += 4;
995
996			if (!z.avail_in && done_reading) {
997				goto stop;
998			}
999			state++;
1000			break;
1001		case GZSTATE_LEN:
1002			{
1003				uLong origlen;
1004
1005				if (z.avail_in < 4) {
1006					if (!done_reading) {
1007						needmore = 1;
1008						continue;
1009					}
1010					maybe_warnx("truncated input");
1011					goto stop_and_fail;
1012				}
1013				origlen = ((unsigned)z.next_in[0] & 0xff) |
1014					((unsigned)z.next_in[1] & 0xff) << 8 |
1015					((unsigned)z.next_in[2] & 0xff) << 16 |
1016					((unsigned)z.next_in[3] & 0xff) << 24;
1017
1018				if (origlen != out_sub_tot) {
1019					maybe_warnx("invalid compressed"
1020					     " data--length error");
1021					goto stop_and_fail;
1022				}
1023			}
1024
1025			z.avail_in -= 4;
1026			z.next_in += 4;
1027
1028			if (error < 0) {
1029				maybe_warnx("decompression error");
1030				goto stop_and_fail;
1031			}
1032			state = GZSTATE_MAGIC0;
1033			break;
1034		}
1035		continue;
1036stop_and_fail:
1037		out_tot = -1;
1038stop:
1039		break;
1040	}
1041	if (state > GZSTATE_INIT)
1042		inflateEnd(&z);
1043
1044	free(inbufp);
1045out1:
1046	free(outbufp);
1047out2:
1048	if (gsizep)
1049		*gsizep = in_tot;
1050	return (out_tot);
1051}
1052
1053#ifndef SMALL
1054/*
1055 * set the owner, mode, flags & utimes using the given file descriptor.
1056 * file is only used in possible warning messages.
1057 */
1058static void
1059copymodes(int fd, const struct stat *sbp, const char *file)
1060{
1061	struct timeval times[2];
1062	struct stat sb;
1063
1064	/*
1065	 * If we have no info on the input, give this file some
1066	 * default values and return..
1067	 */
1068	if (sbp == NULL) {
1069		mode_t mask = umask(022);
1070
1071		(void)fchmod(fd, DEFFILEMODE & ~mask);
1072		(void)umask(mask);
1073		return;
1074	}
1075	sb = *sbp;
1076
1077	/* if the chown fails, remove set-id bits as-per compress(1) */
1078	if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1079		if (errno != EPERM)
1080			maybe_warn("couldn't fchown: %s", file);
1081		sb.st_mode &= ~(S_ISUID|S_ISGID);
1082	}
1083
1084	/* we only allow set-id and the 9 normal permission bits */
1085	sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1086	if (fchmod(fd, sb.st_mode) < 0)
1087		maybe_warn("couldn't fchmod: %s", file);
1088
1089	TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atim);
1090	TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtim);
1091	if (futimes(fd, times) < 0)
1092		maybe_warn("couldn't utimes: %s", file);
1093
1094	/* only try flags if they exist already */
1095        if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1096		maybe_warn("couldn't fchflags: %s", file);
1097}
1098#endif
1099
1100/* what sort of file is this? */
1101static enum filetype
1102file_gettype(u_char *buf)
1103{
1104
1105	if (buf[0] == GZIP_MAGIC0 &&
1106	    (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1107		return FT_GZIP;
1108	else
1109#ifndef NO_BZIP2_SUPPORT
1110	if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1111	    buf[3] >= '0' && buf[3] <= '9')
1112		return FT_BZIP2;
1113	else
1114#endif
1115#ifndef NO_COMPRESS_SUPPORT
1116	if (memcmp(buf, Z_MAGIC, 2) == 0)
1117		return FT_Z;
1118	else
1119#endif
1120#ifndef NO_PACK_SUPPORT
1121	if (memcmp(buf, PACK_MAGIC, 2) == 0)
1122		return FT_PACK;
1123	else
1124#endif
1125		return FT_UNKNOWN;
1126}
1127
1128#ifndef SMALL
1129/* check the outfile is OK. */
1130static int
1131check_outfile(const char *outfile)
1132{
1133	struct stat sb;
1134	int ok = 1;
1135
1136	if (lflag == 0 && stat(outfile, &sb) == 0) {
1137		if (fflag)
1138			unlink(outfile);
1139		else if (isatty(STDIN_FILENO)) {
1140			char ans[10] = { 'n', '\0' };	/* default */
1141
1142			fprintf(stderr, "%s already exists -- do you wish to "
1143					"overwrite (y or n)? " , outfile);
1144			(void)fgets(ans, sizeof(ans) - 1, stdin);
1145			if (ans[0] != 'y' && ans[0] != 'Y') {
1146				fprintf(stderr, "\tnot overwriting\n");
1147				ok = 0;
1148			} else
1149				unlink(outfile);
1150		} else {
1151			maybe_warnx("%s already exists -- skipping", outfile);
1152			ok = 0;
1153		}
1154	}
1155	return ok;
1156}
1157
1158static void
1159unlink_input(const char *file, const struct stat *sb)
1160{
1161	struct stat nsb;
1162
1163	if (kflag)
1164		return;
1165	if (stat(file, &nsb) != 0)
1166		/* Must be gone already */
1167		return;
1168	if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1169		/* Definitely a different file */
1170		return;
1171	unlink(file);
1172}
1173
1174static void
1175sigint_handler(int signo __unused)
1176{
1177
1178	if (remove_file != NULL)
1179		unlink(remove_file);
1180	_exit(2);
1181}
1182#endif
1183
1184static const suffixes_t *
1185check_suffix(char *file, int xlate)
1186{
1187	const suffixes_t *s;
1188	int len = strlen(file);
1189	char *sp;
1190
1191	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1192		/* if it doesn't fit in "a.suf", don't bother */
1193		if (s->ziplen >= len)
1194			continue;
1195		sp = file + len - s->ziplen;
1196		if (strcmp(s->zipped, sp) != 0)
1197			continue;
1198		if (xlate)
1199			strcpy(sp, s->normal);
1200		return s;
1201	}
1202	return NULL;
1203}
1204
1205/*
1206 * compress the given file: create a corresponding .gz file and remove the
1207 * original.
1208 */
1209static off_t
1210file_compress(char *file, char *outfile, size_t outsize)
1211{
1212	int in;
1213	int out;
1214	off_t size, insize;
1215#ifndef SMALL
1216	struct stat isb, osb;
1217	const suffixes_t *suff;
1218#endif
1219
1220	in = open(file, O_RDONLY);
1221	if (in == -1) {
1222		maybe_warn("can't open %s", file);
1223		return (-1);
1224	}
1225
1226#ifndef SMALL
1227	if (fstat(in, &isb) != 0) {
1228		maybe_warn("couldn't stat: %s", file);
1229		close(in);
1230		return (-1);
1231	}
1232#endif
1233
1234	if (cflag == 0) {
1235#ifndef SMALL
1236		if (isb.st_nlink > 1 && fflag == 0) {
1237			maybe_warnx("%s has %d other link%s -- skipping",
1238			    file, isb.st_nlink - 1,
1239			    (isb.st_nlink - 1) == 1 ? "" : "s");
1240			close(in);
1241			return (-1);
1242		}
1243
1244		if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1245		    suff->zipped[0] != 0) {
1246			maybe_warnx("%s already has %s suffix -- unchanged",
1247			    file, suff->zipped);
1248			close(in);
1249			return (-1);
1250		}
1251#endif
1252
1253		/* Add (usually) .gz to filename */
1254		if ((size_t)snprintf(outfile, outsize, "%s%s",
1255		    file, suffixes[0].zipped) >= outsize)
1256			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1257			    suffixes[0].zipped, suffixes[0].ziplen + 1);
1258
1259#ifndef SMALL
1260		if (check_outfile(outfile) == 0) {
1261			close(in);
1262			return (-1);
1263		}
1264#endif
1265	}
1266
1267	if (cflag == 0) {
1268		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1269		if (out == -1) {
1270			maybe_warn("could not create output: %s", outfile);
1271			fclose(stdin);
1272			return (-1);
1273		}
1274#ifndef SMALL
1275		remove_file = outfile;
1276#endif
1277	} else
1278		out = STDOUT_FILENO;
1279
1280	insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1281
1282	(void)close(in);
1283
1284	/*
1285	 * If there was an error, insize will be -1.
1286	 * If we compressed to stdout, just return the size.
1287	 * Otherwise stat the file and check it is the correct size.
1288	 * We only blow away the file if we can stat the output and it
1289	 * has the expected size.
1290	 */
1291	if (cflag != 0)
1292		return (insize == -1 ? -1 : size);
1293
1294#ifndef SMALL
1295	if (fstat(out, &osb) != 0) {
1296		maybe_warn("couldn't stat: %s", outfile);
1297		goto bad_outfile;
1298	}
1299
1300	if (osb.st_size != size) {
1301		maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1302		    outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1303		goto bad_outfile;
1304	}
1305
1306	copymodes(out, &isb, outfile);
1307	remove_file = NULL;
1308#endif
1309	if (close(out) == -1)
1310		maybe_warn("couldn't close output");
1311
1312	/* output is good, ok to delete input */
1313	unlink_input(file, &isb);
1314	return (size);
1315
1316#ifndef SMALL
1317    bad_outfile:
1318	if (close(out) == -1)
1319		maybe_warn("couldn't close output");
1320
1321	maybe_warnx("leaving original %s", file);
1322	unlink(outfile);
1323	return (size);
1324#endif
1325}
1326
1327/* uncompress the given file and remove the original */
1328static off_t
1329file_uncompress(char *file, char *outfile, size_t outsize)
1330{
1331	struct stat isb, osb;
1332	off_t size;
1333	ssize_t rbytes;
1334	unsigned char header1[4];
1335	enum filetype method;
1336	int fd, ofd, zfd = -1;
1337#ifndef SMALL
1338	ssize_t rv;
1339	time_t timestamp = 0;
1340	unsigned char name[PATH_MAX + 1];
1341#endif
1342
1343	/* gather the old name info */
1344
1345	fd = open(file, O_RDONLY);
1346	if (fd < 0) {
1347		maybe_warn("can't open %s", file);
1348		goto lose;
1349	}
1350
1351	strlcpy(outfile, file, outsize);
1352	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1353		maybe_warnx("%s: unknown suffix -- ignored", file);
1354		goto lose;
1355	}
1356
1357	rbytes = read(fd, header1, sizeof header1);
1358	if (rbytes != sizeof header1) {
1359		/* we don't want to fail here. */
1360#ifndef SMALL
1361		if (fflag)
1362			goto lose;
1363#endif
1364		if (rbytes == -1)
1365			maybe_warn("can't read %s", file);
1366		else
1367			goto unexpected_EOF;
1368		goto lose;
1369	}
1370
1371	method = file_gettype(header1);
1372
1373#ifndef SMALL
1374	if (fflag == 0 && method == FT_UNKNOWN) {
1375		maybe_warnx("%s: not in gzip format", file);
1376		goto lose;
1377	}
1378
1379#endif
1380
1381#ifndef SMALL
1382	if (method == FT_GZIP && Nflag) {
1383		unsigned char ts[4];	/* timestamp */
1384
1385		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1386		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1387			goto unexpected_EOF;
1388		if (rv == -1) {
1389			if (!fflag)
1390				maybe_warn("can't read %s", file);
1391			goto lose;
1392		}
1393		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1394
1395		if (header1[3] & ORIG_NAME) {
1396			rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1397			if (rbytes < 0) {
1398				maybe_warn("can't read %s", file);
1399				goto lose;
1400			}
1401			if (name[0] != 0) {
1402				/* preserve original directory name */
1403				char *dp = strrchr(file, '/');
1404				if (dp == NULL)
1405					dp = file;
1406				else
1407					dp++;
1408				snprintf(outfile, outsize, "%.*s%.*s",
1409						(int) (dp - file),
1410						file, (int) rbytes, name);
1411			}
1412		}
1413	}
1414#endif
1415	lseek(fd, 0, SEEK_SET);
1416
1417	if (cflag == 0 || lflag) {
1418		if (fstat(fd, &isb) != 0)
1419			goto lose;
1420#ifndef SMALL
1421		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1422			maybe_warnx("%s has %d other links -- skipping",
1423			    file, isb.st_nlink - 1);
1424			goto lose;
1425		}
1426		if (nflag == 0 && timestamp)
1427			isb.st_mtime = timestamp;
1428		if (check_outfile(outfile) == 0)
1429			goto lose;
1430#endif
1431	}
1432
1433	if (cflag == 0 && lflag == 0) {
1434		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1435		if (zfd == STDOUT_FILENO) {
1436			/* We won't close STDOUT_FILENO later... */
1437			zfd = dup(zfd);
1438			close(STDOUT_FILENO);
1439		}
1440		if (zfd == -1) {
1441			maybe_warn("can't open %s", outfile);
1442			goto lose;
1443		}
1444#ifndef SMALL
1445		remove_file = outfile;
1446#endif
1447	} else
1448		zfd = STDOUT_FILENO;
1449
1450#ifndef NO_BZIP2_SUPPORT
1451	if (method == FT_BZIP2) {
1452
1453		/* XXX */
1454		if (lflag) {
1455			maybe_warnx("no -l with bzip2 files");
1456			goto lose;
1457		}
1458
1459		size = unbzip2(fd, zfd, NULL, 0, NULL);
1460	} else
1461#endif
1462
1463#ifndef NO_COMPRESS_SUPPORT
1464	if (method == FT_Z) {
1465		FILE *in, *out;
1466
1467		/* XXX */
1468		if (lflag) {
1469			maybe_warnx("no -l with Lempel-Ziv files");
1470			goto lose;
1471		}
1472
1473		if ((in = zdopen(fd)) == NULL) {
1474			maybe_warn("zdopen for read: %s", file);
1475			goto lose;
1476		}
1477
1478		out = fdopen(dup(zfd), "w");
1479		if (out == NULL) {
1480			maybe_warn("fdopen for write: %s", outfile);
1481			fclose(in);
1482			goto lose;
1483		}
1484
1485		size = zuncompress(in, out, NULL, 0, NULL);
1486		/* need to fclose() if ferror() is true... */
1487		if (ferror(in) | fclose(in)) {
1488			maybe_warn("failed infile fclose");
1489			unlink(outfile);
1490			(void)fclose(out);
1491		}
1492		if (fclose(out) != 0) {
1493			maybe_warn("failed outfile fclose");
1494			unlink(outfile);
1495			goto lose;
1496		}
1497	} else
1498#endif
1499
1500#ifndef NO_PACK_SUPPORT
1501	if (method == FT_PACK) {
1502		if (lflag) {
1503			maybe_warnx("no -l with packed files");
1504			goto lose;
1505		}
1506
1507		size = unpack(fd, zfd, NULL, 0, NULL);
1508	} else
1509#endif
1510
1511#ifndef SMALL
1512	if (method == FT_UNKNOWN) {
1513		if (lflag) {
1514			maybe_warnx("no -l for unknown filetypes");
1515			goto lose;
1516		}
1517		size = cat_fd(NULL, 0, NULL, fd);
1518	} else
1519#endif
1520	{
1521		if (lflag) {
1522			print_list(fd, isb.st_size, outfile, isb.st_mtime);
1523			close(fd);
1524			return -1;	/* XXX */
1525		}
1526
1527		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1528	}
1529
1530	if (close(fd) != 0)
1531		maybe_warn("couldn't close input");
1532	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1533		maybe_warn("couldn't close output");
1534
1535	if (size == -1) {
1536		if (cflag == 0)
1537			unlink(outfile);
1538		maybe_warnx("%s: uncompress failed", file);
1539		return -1;
1540	}
1541
1542	/* if testing, or we uncompressed to stdout, this is all we need */
1543#ifndef SMALL
1544	if (tflag)
1545		return size;
1546#endif
1547	/* if we are uncompressing to stdin, don't remove the file. */
1548	if (cflag)
1549		return size;
1550
1551	/*
1552	 * if we create a file...
1553	 */
1554	/*
1555	 * if we can't stat the file don't remove the file.
1556	 */
1557
1558	ofd = open(outfile, O_RDWR, 0);
1559	if (ofd == -1) {
1560		maybe_warn("couldn't open (leaving original): %s",
1561			   outfile);
1562		return -1;
1563	}
1564	if (fstat(ofd, &osb) != 0) {
1565		maybe_warn("couldn't stat (leaving original): %s",
1566			   outfile);
1567		close(ofd);
1568		return -1;
1569	}
1570	if (osb.st_size != size) {
1571		maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1572		    (uintmax_t)size, (uintmax_t)osb.st_size);
1573		close(ofd);
1574		unlink(outfile);
1575		return -1;
1576	}
1577#ifndef SMALL
1578	copymodes(ofd, &isb, outfile);
1579	remove_file = NULL;
1580#endif
1581	close(ofd);
1582	unlink_input(file, &isb);
1583	return size;
1584
1585    unexpected_EOF:
1586	maybe_warnx("%s: unexpected end of file", file);
1587    lose:
1588	if (fd != -1)
1589		close(fd);
1590	if (zfd != -1 && zfd != STDOUT_FILENO)
1591		close(fd);
1592	return -1;
1593}
1594
1595#ifndef SMALL
1596static off_t
1597cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1598{
1599	char buf[BUFLEN];
1600	off_t in_tot;
1601	ssize_t w;
1602
1603	in_tot = count;
1604	w = write(STDOUT_FILENO, prepend, count);
1605	if (w == -1 || (size_t)w != count) {
1606		maybe_warn("write to stdout");
1607		return -1;
1608	}
1609	for (;;) {
1610		ssize_t rv;
1611
1612		rv = read(fd, buf, sizeof buf);
1613		if (rv == 0)
1614			break;
1615		if (rv < 0) {
1616			maybe_warn("read from fd %d", fd);
1617			break;
1618		}
1619
1620		if (write(STDOUT_FILENO, buf, rv) != rv) {
1621			maybe_warn("write to stdout");
1622			break;
1623		}
1624		in_tot += rv;
1625	}
1626
1627	if (gsizep)
1628		*gsizep = in_tot;
1629	return (in_tot);
1630}
1631#endif
1632
1633static void
1634handle_stdin(void)
1635{
1636	unsigned char header1[4];
1637	off_t usize, gsize;
1638	enum filetype method;
1639	ssize_t bytes_read;
1640#ifndef NO_COMPRESS_SUPPORT
1641	FILE *in;
1642#endif
1643
1644#ifndef SMALL
1645	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1646		maybe_warnx("standard input is a terminal -- ignoring");
1647		return;
1648	}
1649#endif
1650
1651	if (lflag) {
1652		struct stat isb;
1653
1654		/* XXX could read the whole file, etc. */
1655		if (fstat(STDIN_FILENO, &isb) < 0) {
1656			maybe_warn("fstat");
1657			return;
1658		}
1659		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1660		return;
1661	}
1662
1663	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1664	if (bytes_read == -1) {
1665		maybe_warn("can't read stdin");
1666		return;
1667	} else if (bytes_read != sizeof(header1)) {
1668		maybe_warnx("(stdin): unexpected end of file");
1669		return;
1670	}
1671
1672	method = file_gettype(header1);
1673	switch (method) {
1674	default:
1675#ifndef SMALL
1676		if (fflag == 0) {
1677			maybe_warnx("unknown compression format");
1678			return;
1679		}
1680		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1681		break;
1682#endif
1683	case FT_GZIP:
1684		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1685			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1686		break;
1687#ifndef NO_BZIP2_SUPPORT
1688	case FT_BZIP2:
1689		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1690				(char *)header1, sizeof header1, &gsize);
1691		break;
1692#endif
1693#ifndef NO_COMPRESS_SUPPORT
1694	case FT_Z:
1695		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1696			maybe_warnx("zopen of stdin");
1697			return;
1698		}
1699
1700		usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize);
1701		fclose(in);
1702		break;
1703#endif
1704#ifndef NO_PACK_SUPPORT
1705	case FT_PACK:
1706		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1707			       (char *)header1, sizeof header1, &gsize);
1708		break;
1709#endif
1710	}
1711
1712#ifndef SMALL
1713        if (vflag && !tflag && usize != -1 && gsize != -1)
1714		print_verbage(NULL, NULL, usize, gsize);
1715	if (vflag && tflag)
1716		print_test("(stdin)", usize != -1);
1717#endif
1718
1719}
1720
1721static void
1722handle_stdout(void)
1723{
1724	off_t gsize, usize;
1725	struct stat sb;
1726	time_t systime;
1727	uint32_t mtime;
1728	int ret;
1729
1730#ifndef SMALL
1731	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1732		maybe_warnx("standard output is a terminal -- ignoring");
1733		return;
1734	}
1735#endif
1736	/* If stdin is a file use it's mtime, otherwise use current time */
1737	ret = fstat(STDIN_FILENO, &sb);
1738
1739#ifndef SMALL
1740	if (ret < 0) {
1741		maybe_warn("Can't stat stdin");
1742		return;
1743	}
1744#endif
1745
1746	if (S_ISREG(sb.st_mode))
1747		mtime = (uint32_t)sb.st_mtime;
1748	else {
1749		systime = time(NULL);
1750#ifndef SMALL
1751		if (systime == -1) {
1752			maybe_warn("time");
1753			return;
1754		}
1755#endif
1756		mtime = (uint32_t)systime;
1757	}
1758
1759	usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1760#ifndef SMALL
1761        if (vflag && !tflag && usize != -1 && gsize != -1)
1762		print_verbage(NULL, NULL, usize, gsize);
1763#endif
1764}
1765
1766/* do what is asked for, for the path name */
1767static void
1768handle_pathname(char *path)
1769{
1770	char *opath = path, *s = NULL;
1771	ssize_t len;
1772	int slen;
1773	struct stat sb;
1774
1775	/* check for stdout/stdin */
1776	if (path[0] == '-' && path[1] == '\0') {
1777		if (dflag)
1778			handle_stdin();
1779		else
1780			handle_stdout();
1781		return;
1782	}
1783
1784retry:
1785	if (stat(path, &sb) != 0) {
1786		/* lets try <path>.gz if we're decompressing */
1787		if (dflag && s == NULL && errno == ENOENT) {
1788			len = strlen(path);
1789			slen = suffixes[0].ziplen;
1790			s = malloc(len + slen + 1);
1791			if (s == NULL)
1792				maybe_err("malloc");
1793			memcpy(s, path, len);
1794			memcpy(s + len, suffixes[0].zipped, slen + 1);
1795			path = s;
1796			goto retry;
1797		}
1798		maybe_warn("can't stat: %s", opath);
1799		goto out;
1800	}
1801
1802	if (S_ISDIR(sb.st_mode)) {
1803#ifndef SMALL
1804		if (rflag)
1805			handle_dir(path);
1806		else
1807#endif
1808			maybe_warnx("%s is a directory", path);
1809		goto out;
1810	}
1811
1812	if (S_ISREG(sb.st_mode))
1813		handle_file(path, &sb);
1814	else
1815		maybe_warnx("%s is not a regular file", path);
1816
1817out:
1818	if (s)
1819		free(s);
1820}
1821
1822/* compress/decompress a file */
1823static void
1824handle_file(char *file, struct stat *sbp)
1825{
1826	off_t usize, gsize;
1827	char	outfile[PATH_MAX];
1828
1829	infile = file;
1830	if (dflag) {
1831		usize = file_uncompress(file, outfile, sizeof(outfile));
1832#ifndef SMALL
1833		if (vflag && tflag)
1834			print_test(file, usize != -1);
1835#endif
1836		if (usize == -1)
1837			return;
1838		gsize = sbp->st_size;
1839	} else {
1840		gsize = file_compress(file, outfile, sizeof(outfile));
1841		if (gsize == -1)
1842			return;
1843		usize = sbp->st_size;
1844	}
1845
1846
1847#ifndef SMALL
1848	if (vflag && !tflag)
1849		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1850#endif
1851}
1852
1853#ifndef SMALL
1854/* this is used with -r to recursively descend directories */
1855static void
1856handle_dir(char *dir)
1857{
1858	char *path_argv[2];
1859	FTS *fts;
1860	FTSENT *entry;
1861
1862	path_argv[0] = dir;
1863	path_argv[1] = 0;
1864	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1865	if (fts == NULL) {
1866		warn("couldn't fts_open %s", dir);
1867		return;
1868	}
1869
1870	while ((entry = fts_read(fts))) {
1871		switch(entry->fts_info) {
1872		case FTS_D:
1873		case FTS_DP:
1874			continue;
1875
1876		case FTS_DNR:
1877		case FTS_ERR:
1878		case FTS_NS:
1879			maybe_warn("%s", entry->fts_path);
1880			continue;
1881		case FTS_F:
1882			handle_file(entry->fts_path, entry->fts_statp);
1883		}
1884	}
1885	(void)fts_close(fts);
1886}
1887#endif
1888
1889/* print a ratio - size reduction as a fraction of uncompressed size */
1890static void
1891print_ratio(off_t in, off_t out, FILE *where)
1892{
1893	int percent10;	/* 10 * percent */
1894	off_t diff;
1895	char buff[8];
1896	int len;
1897
1898	diff = in - out/2;
1899	if (diff <= 0)
1900		/*
1901		 * Output is more than double size of input! print -99.9%
1902		 * Quite possibly we've failed to get the original size.
1903		 */
1904		percent10 = -999;
1905	else {
1906		/*
1907		 * We only need 12 bits of result from the final division,
1908		 * so reduce the values until a 32bit division will suffice.
1909		 */
1910		while (in > 0x100000) {
1911			diff >>= 1;
1912			in >>= 1;
1913		}
1914		if (in != 0)
1915			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1916		else
1917			percent10 = 0;
1918	}
1919
1920	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1921	/* Move the '.' to before the last digit */
1922	buff[len - 1] = buff[len - 2];
1923	buff[len - 2] = '.';
1924	fprintf(where, "%5s%%", buff);
1925}
1926
1927#ifndef SMALL
1928/* print compression statistics, and the new name (if there is one!) */
1929static void
1930print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1931{
1932	if (file)
1933		fprintf(stderr, "%s:%s  ", file,
1934		    strlen(file) < 7 ? "\t\t" : "\t");
1935	print_ratio(usize, gsize, stderr);
1936	if (nfile)
1937		fprintf(stderr, " -- replaced with %s", nfile);
1938	fprintf(stderr, "\n");
1939	fflush(stderr);
1940}
1941
1942/* print test results */
1943static void
1944print_test(const char *file, int ok)
1945{
1946
1947	if (exit_value == 0 && ok == 0)
1948		exit_value = 1;
1949	fprintf(stderr, "%s:%s  %s\n", file,
1950	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1951	fflush(stderr);
1952}
1953#endif
1954
1955/* print a file's info ala --list */
1956/* eg:
1957  compressed uncompressed  ratio uncompressed_name
1958      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1959*/
1960static void
1961print_list(int fd, off_t out, const char *outfile, time_t ts)
1962{
1963	static int first = 1;
1964#ifndef SMALL
1965	static off_t in_tot, out_tot;
1966	uint32_t crc = 0;
1967#endif
1968	off_t in = 0, rv;
1969
1970	if (first) {
1971#ifndef SMALL
1972		if (vflag)
1973			printf("method  crc     date  time  ");
1974#endif
1975		if (qflag == 0)
1976			printf("  compressed uncompressed  "
1977			       "ratio uncompressed_name\n");
1978	}
1979	first = 0;
1980
1981	/* print totals? */
1982#ifndef SMALL
1983	if (fd == -1) {
1984		in = in_tot;
1985		out = out_tot;
1986	} else
1987#endif
1988	{
1989		/* read the last 4 bytes - this is the uncompressed size */
1990		rv = lseek(fd, (off_t)(-8), SEEK_END);
1991		if (rv != -1) {
1992			unsigned char buf[8];
1993			uint32_t usize;
1994
1995			rv = read(fd, (char *)buf, sizeof(buf));
1996			if (rv == -1)
1997				maybe_warn("read of uncompressed size");
1998			else if (rv != sizeof(buf))
1999				maybe_warnx("read of uncompressed size");
2000
2001			else {
2002				usize = buf[4] | buf[5] << 8 |
2003					buf[6] << 16 | buf[7] << 24;
2004				in = (off_t)usize;
2005#ifndef SMALL
2006				crc = buf[0] | buf[1] << 8 |
2007				      buf[2] << 16 | buf[3] << 24;
2008#endif
2009			}
2010		}
2011	}
2012
2013#ifndef SMALL
2014	if (vflag && fd == -1)
2015		printf("                            ");
2016	else if (vflag) {
2017		char *date = ctime(&ts);
2018
2019		/* skip the day, 1/100th second, and year */
2020		date += 4;
2021		date[12] = 0;
2022		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2023	}
2024	in_tot += in;
2025	out_tot += out;
2026#else
2027	(void)&ts;	/* XXX */
2028#endif
2029	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2030	print_ratio(in, out, stdout);
2031	printf(" %s\n", outfile);
2032}
2033
2034/* display the usage of NetBSD gzip */
2035static void
2036usage(void)
2037{
2038
2039	fprintf(stderr, "%s\n", gzip_version);
2040	fprintf(stderr,
2041#ifdef SMALL
2042    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2043#else
2044    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2045    " -1 --fast            fastest (worst) compression\n"
2046    " -2 .. -8             set compression level\n"
2047    " -9 --best            best (slowest) compression\n"
2048    " -c --stdout          write to stdout, keep original files\n"
2049    "    --to-stdout\n"
2050    " -d --decompress      uncompress files\n"
2051    "    --uncompress\n"
2052    " -f --force           force overwriting & compress links\n"
2053    " -h --help            display this help\n"
2054    " -k --keep            don't delete input files during operation\n"
2055    " -l --list            list compressed file contents\n"
2056    " -N --name            save or restore original file name and time stamp\n"
2057    " -n --no-name         don't save original file name or time stamp\n"
2058    " -q --quiet           output no warnings\n"
2059    " -r --recursive       recursively compress files in directories\n"
2060    " -S .suf              use suffix .suf instead of .gz\n"
2061    "    --suffix .suf\n"
2062    " -t --test            test compressed file\n"
2063    " -V --version         display program version\n"
2064    " -v --verbose         print extra statistics\n",
2065#endif
2066	    getprogname());
2067	exit(0);
2068}
2069
2070#ifndef SMALL
2071/* display the license information of FreeBSD gzip */
2072static void
2073display_license(void)
2074{
2075
2076	fprintf(stderr, "%s (based on NetBSD gzip 20091011)\n", gzip_version);
2077	fprintf(stderr, "%s\n", gzip_copyright);
2078	exit(0);
2079}
2080#endif
2081
2082/* display the version of NetBSD gzip */
2083static void
2084display_version(void)
2085{
2086
2087	fprintf(stderr, "%s\n", gzip_version);
2088	exit(0);
2089}
2090
2091#ifndef NO_BZIP2_SUPPORT
2092#include "unbzip2.c"
2093#endif
2094#ifndef NO_COMPRESS_SUPPORT
2095#include "zuncompress.c"
2096#endif
2097#ifndef NO_PACK_SUPPORT
2098#include "unpack.c"
2099#endif
2100
2101static ssize_t
2102read_retry(int fd, void *buf, size_t sz)
2103{
2104	char *cp = buf;
2105	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2106
2107	while (left > 0) {
2108		ssize_t ret;
2109
2110		ret = read(fd, cp, left);
2111		if (ret == -1) {
2112			return ret;
2113		} else if (ret == 0) {
2114			break; /* EOF */
2115		}
2116		cp += ret;
2117		left -= ret;
2118	}
2119
2120	return sz - left;
2121}
2122