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