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