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