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