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