gzip.c revision 206387
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 206387 2010-04-07 22:54:53Z 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 *	- handle some signals better (remove outfile?)
47 *	- make bzip2/compress -v/-t/-l support work as well as possible
48 */
49
50#include <sys/param.h>
51#include <sys/stat.h>
52#include <sys/time.h>
53
54#include <inttypes.h>
55#include <unistd.h>
56#include <stdio.h>
57#include <string.h>
58#include <stdlib.h>
59#include <err.h>
60#include <errno.h>
61#include <fcntl.h>
62#include <zlib.h>
63#include <fts.h>
64#include <libgen.h>
65#include <stdarg.h>
66#include <getopt.h>
67#include <time.h>
68
69#ifndef PRIdOFF
70#define PRIdOFF PRId64
71#endif
72
73/* what type of file are we dealing with */
74enum filetype {
75	FT_GZIP,
76#ifndef NO_BZIP2_SUPPORT
77	FT_BZIP2,
78#endif
79#ifndef NO_COMPRESS_SUPPORT
80	FT_Z,
81#endif
82#ifndef NO_PACK_SUPPORT
83	FT_PACK,
84#endif
85	FT_LAST,
86	FT_UNKNOWN
87};
88
89#ifndef NO_BZIP2_SUPPORT
90#include <bzlib.h>
91
92#define BZ2_SUFFIX	".bz2"
93#define BZIP2_MAGIC	"\102\132\150"
94#endif
95
96#ifndef NO_COMPRESS_SUPPORT
97#define Z_SUFFIX	".Z"
98#define Z_MAGIC		"\037\235"
99#endif
100
101#ifndef NO_PACK_SUPPORT
102#define PACK_MAGIC	"\037\036"
103#endif
104
105#define GZ_SUFFIX	".gz"
106
107#define BUFLEN		(64 * 1024)
108
109#define GZIP_MAGIC0	0x1F
110#define GZIP_MAGIC1	0x8B
111#define GZIP_OMAGIC1	0x9E
112
113#define GZIP_TIMESTAMP	(off_t)4
114#define GZIP_ORIGNAME	(off_t)10
115
116#define HEAD_CRC	0x02
117#define EXTRA_FIELD	0x04
118#define ORIG_NAME	0x08
119#define COMMENT		0x10
120
121#define OS_CODE		3	/* Unix */
122
123typedef struct {
124    const char	*zipped;
125    int		ziplen;
126    const char	*normal;	/* for unzip - must not be longer than zipped */
127} suffixes_t;
128static suffixes_t suffixes[] = {
129#define	SUFFIX(Z, N) {Z, sizeof Z - 1, N}
130	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S .xxx */
131#ifndef SMALL
132	SUFFIX(GZ_SUFFIX,	""),
133	SUFFIX(".z",		""),
134	SUFFIX("-gz",		""),
135	SUFFIX("-z",		""),
136	SUFFIX("_z",		""),
137	SUFFIX(".taz",		".tar"),
138	SUFFIX(".tgz",		".tar"),
139#ifndef NO_BZIP2_SUPPORT
140	SUFFIX(BZ2_SUFFIX,	""),
141	SUFFIX(".tbz",		".tar"),
142	SUFFIX(".tbz2",		".tar"),
143#endif
144#ifndef NO_COMPRESS_SUPPORT
145	SUFFIX(Z_SUFFIX,	""),
146#endif
147	SUFFIX(GZ_SUFFIX,	""),	/* Overwritten by -S "" */
148#endif /* SMALL */
149#undef SUFFIX
150};
151#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
152#define SUFFIX_MAXLEN	30
153
154static	const char	gzip_version[] = "FreeBSD gzip 20100407";
155
156#ifndef SMALL
157static	const char	gzip_copyright[] = \
158"   Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
159"   All rights reserved.\n"
160"\n"
161"   Redistribution and use in source and binary forms, with or without\n"
162"   modification, are permitted provided that the following conditions\n"
163"   are met:\n"
164"   1. Redistributions of source code must retain the above copyright\n"
165"      notice, this list of conditions and the following disclaimer.\n"
166"   2. Redistributions in binary form must reproduce the above copyright\n"
167"      notice, this list of conditions and the following disclaimer in the\n"
168"      documentation and/or other materials provided with the distribution.\n"
169"\n"
170"   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
171"   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
172"   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
173"   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
174"   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
175"   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
176"   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
177"   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
178"   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
179"   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
180"   SUCH DAMAGE.";
181#endif
182
183static	int	cflag;			/* stdout mode */
184static	int	dflag;			/* decompress mode */
185static	int	lflag;			/* list mode */
186static	int	numflag = 6;		/* gzip -1..-9 value */
187
188#ifndef SMALL
189static	int	fflag;			/* force mode */
190static	int	kflag;			/* don't delete input files */
191static	int	nflag;			/* don't save name/timestamp */
192static	int	Nflag;			/* don't restore name/timestamp */
193static	int	qflag;			/* quiet mode */
194static	int	rflag;			/* recursive mode */
195static	int	tflag;			/* test */
196static	int	vflag;			/* verbose mode */
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);
234#endif
235static	const suffixes_t *check_suffix(char *, int);
236static	ssize_t	read_retry(int, void *, size_t);
237
238#ifdef SMALL
239#define unlink_input(f, sb) unlink(f)
240#else
241static	off_t	cat_fd(unsigned char *, size_t, off_t *, int fd);
242static	void	prepend_gzip(char *, int *, char ***);
243static	void	handle_dir(char *);
244static	void	print_verbage(const char *, const char *, off_t, off_t);
245static	void	print_test(const char *, int);
246static	void	copymodes(int fd, const struct stat *, const char *file);
247static	int	check_outfile(const char *outfile);
248#endif
249
250#ifndef NO_BZIP2_SUPPORT
251static	off_t	unbzip2(int, int, char *, size_t, off_t *);
252#endif
253
254#ifndef NO_COMPRESS_SUPPORT
255static	FILE 	*zdopen(int);
256static	off_t	zuncompress(FILE *, FILE *, char *, size_t, off_t *);
257#endif
258
259#ifndef NO_PACK_SUPPORT
260static	off_t	unpack(int, int, char *, size_t, off_t *);
261#endif
262
263int main(int, char **p);
264
265#ifdef SMALL
266#define getopt_long(a,b,c,d,e) getopt(a,b,c)
267#else
268static const struct option longopts[] = {
269	{ "stdout",		no_argument,		0,	'c' },
270	{ "to-stdout",		no_argument,		0,	'c' },
271	{ "decompress",		no_argument,		0,	'd' },
272	{ "uncompress",		no_argument,		0,	'd' },
273	{ "force",		no_argument,		0,	'f' },
274	{ "help",		no_argument,		0,	'h' },
275	{ "keep",		no_argument,		0,	'k' },
276	{ "list",		no_argument,		0,	'l' },
277	{ "no-name",		no_argument,		0,	'n' },
278	{ "name",		no_argument,		0,	'N' },
279	{ "quiet",		no_argument,		0,	'q' },
280	{ "recursive",		no_argument,		0,	'r' },
281	{ "suffix",		required_argument,	0,	'S' },
282	{ "test",		no_argument,		0,	't' },
283	{ "verbose",		no_argument,		0,	'v' },
284	{ "version",		no_argument,		0,	'V' },
285	{ "fast",		no_argument,		0,	'1' },
286	{ "best",		no_argument,		0,	'9' },
287	{ "ascii",		no_argument,		0,	'a' },
288	{ "license",		no_argument,		0,	'L' },
289	{ NULL,			no_argument,		0,	0 },
290};
291#endif
292
293int
294main(int argc, char **argv)
295{
296	const char *progname = getprogname();
297#ifndef SMALL
298	char *gzip;
299	int len;
300#endif
301	int ch;
302
303	/* XXX set up signals */
304
305#ifndef SMALL
306	if ((gzip = getenv("GZIP")) != NULL)
307		prepend_gzip(gzip, &argc, &argv);
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#endif
1175
1176static const suffixes_t *
1177check_suffix(char *file, int xlate)
1178{
1179	const suffixes_t *s;
1180	int len = strlen(file);
1181	char *sp;
1182
1183	for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1184		/* if it doesn't fit in "a.suf", don't bother */
1185		if (s->ziplen >= len)
1186			continue;
1187		sp = file + len - s->ziplen;
1188		if (strcmp(s->zipped, sp) != 0)
1189			continue;
1190		if (xlate)
1191			strcpy(sp, s->normal);
1192		return s;
1193	}
1194	return NULL;
1195}
1196
1197/*
1198 * compress the given file: create a corresponding .gz file and remove the
1199 * original.
1200 */
1201static off_t
1202file_compress(char *file, char *outfile, size_t outsize)
1203{
1204	int in;
1205	int out;
1206	off_t size, insize;
1207#ifndef SMALL
1208	struct stat isb, osb;
1209	const suffixes_t *suff;
1210#endif
1211
1212	in = open(file, O_RDONLY);
1213	if (in == -1) {
1214		maybe_warn("can't open %s", file);
1215		return -1;
1216	}
1217
1218	if (cflag == 0) {
1219#ifndef SMALL
1220		if (fstat(in, &isb) == 0) {
1221			if (isb.st_nlink > 1 && fflag == 0) {
1222				maybe_warnx("%s has %d other link%s -- "
1223					    "skipping", file, isb.st_nlink - 1,
1224					    isb.st_nlink == 1 ? "" : "s");
1225				close(in);
1226				return -1;
1227			}
1228		}
1229
1230		if (fflag == 0 && (suff = check_suffix(file, 0))
1231		    && suff->zipped[0] != 0) {
1232			maybe_warnx("%s already has %s suffix -- unchanged",
1233				    file, suff->zipped);
1234			close(in);
1235			return -1;
1236		}
1237#endif
1238
1239		/* Add (usually) .gz to filename */
1240		if ((size_t)snprintf(outfile, outsize, "%s%s",
1241					file, suffixes[0].zipped) >= outsize)
1242			memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1243				suffixes[0].zipped, suffixes[0].ziplen + 1);
1244
1245#ifndef SMALL
1246		if (check_outfile(outfile) == 0) {
1247			close(in);
1248			return -1;
1249		}
1250#endif
1251	}
1252
1253	if (cflag == 0) {
1254		out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1255		if (out == -1) {
1256			maybe_warn("could not create output: %s", outfile);
1257			fclose(stdin);
1258			return -1;
1259		}
1260	} else
1261		out = STDOUT_FILENO;
1262
1263	insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1264
1265	(void)close(in);
1266
1267	/*
1268	 * If there was an error, insize will be -1.
1269	 * If we compressed to stdout, just return the size.
1270	 * Otherwise stat the file and check it is the correct size.
1271	 * We only blow away the file if we can stat the output and it
1272	 * has the expected size.
1273	 */
1274	if (cflag != 0)
1275		return insize == -1 ? -1 : size;
1276
1277#ifndef SMALL
1278	if (fstat(out, &osb) != 0) {
1279		maybe_warn("couldn't stat: %s", outfile);
1280		goto bad_outfile;
1281	}
1282
1283	if (osb.st_size != size) {
1284		maybe_warnx("output file: %s wrong size (%" PRIdOFF
1285				" != %" PRIdOFF "), deleting",
1286				outfile, osb.st_size, size);
1287		goto bad_outfile;
1288	}
1289
1290	copymodes(out, &isb, outfile);
1291#endif
1292	if (close(out) == -1)
1293		maybe_warn("couldn't close output");
1294
1295	/* output is good, ok to delete input */
1296	unlink_input(file, &isb);
1297	return size;
1298
1299#ifndef SMALL
1300    bad_outfile:
1301	if (close(out) == -1)
1302		maybe_warn("couldn't close output");
1303
1304	maybe_warnx("leaving original %s", file);
1305	unlink(outfile);
1306	return size;
1307#endif
1308}
1309
1310/* uncompress the given file and remove the original */
1311static off_t
1312file_uncompress(char *file, char *outfile, size_t outsize)
1313{
1314	struct stat isb, osb;
1315	off_t size;
1316	ssize_t rbytes;
1317	unsigned char header1[4];
1318	enum filetype method;
1319	int fd, ofd, zfd = -1;
1320#ifndef SMALL
1321	ssize_t rv;
1322	time_t timestamp = 0;
1323	unsigned char name[PATH_MAX + 1];
1324#endif
1325
1326	/* gather the old name info */
1327
1328	fd = open(file, O_RDONLY);
1329	if (fd < 0) {
1330		maybe_warn("can't open %s", file);
1331		goto lose;
1332	}
1333
1334	strlcpy(outfile, file, outsize);
1335	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1336		maybe_warnx("%s: unknown suffix -- ignored", file);
1337		goto lose;
1338	}
1339
1340	rbytes = read(fd, header1, sizeof header1);
1341	if (rbytes != sizeof header1) {
1342		/* we don't want to fail here. */
1343#ifndef SMALL
1344		if (fflag)
1345			goto lose;
1346#endif
1347		if (rbytes == -1)
1348			maybe_warn("can't read %s", file);
1349		else
1350			goto unexpected_EOF;
1351		goto lose;
1352	}
1353
1354	method = file_gettype(header1);
1355
1356#ifndef SMALL
1357	if (fflag == 0 && method == FT_UNKNOWN) {
1358		maybe_warnx("%s: not in gzip format", file);
1359		goto lose;
1360	}
1361
1362#endif
1363
1364#ifndef SMALL
1365	if (method == FT_GZIP && Nflag) {
1366		unsigned char ts[4];	/* timestamp */
1367
1368		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1369		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1370			goto unexpected_EOF;
1371		if (rv == -1) {
1372			if (!fflag)
1373				maybe_warn("can't read %s", file);
1374			goto lose;
1375		}
1376		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1377
1378		if (header1[3] & ORIG_NAME) {
1379			rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1380			if (rbytes < 0) {
1381				maybe_warn("can't read %s", file);
1382				goto lose;
1383			}
1384			if (name[0] != 0) {
1385				/* preserve original directory name */
1386				char *dp = strrchr(file, '/');
1387				if (dp == NULL)
1388					dp = file;
1389				else
1390					dp++;
1391				snprintf(outfile, outsize, "%.*s%.*s",
1392						(int) (dp - file),
1393						file, (int) rbytes, name);
1394			}
1395		}
1396	}
1397#endif
1398	lseek(fd, 0, SEEK_SET);
1399
1400	if (cflag == 0 || lflag) {
1401		if (fstat(fd, &isb) != 0)
1402			goto lose;
1403#ifndef SMALL
1404		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1405			maybe_warnx("%s has %d other links -- skipping",
1406			    file, isb.st_nlink - 1);
1407			goto lose;
1408		}
1409		if (nflag == 0 && timestamp)
1410			isb.st_mtime = timestamp;
1411		if (check_outfile(outfile) == 0)
1412			goto lose;
1413#endif
1414	}
1415
1416	if (cflag == 0 && lflag == 0) {
1417		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1418		if (zfd == STDOUT_FILENO) {
1419			/* We won't close STDOUT_FILENO later... */
1420			zfd = dup(zfd);
1421			close(STDOUT_FILENO);
1422		}
1423		if (zfd == -1) {
1424			maybe_warn("can't open %s", outfile);
1425			goto lose;
1426		}
1427	} else
1428		zfd = STDOUT_FILENO;
1429
1430#ifndef NO_BZIP2_SUPPORT
1431	if (method == FT_BZIP2) {
1432
1433		/* XXX */
1434		if (lflag) {
1435			maybe_warnx("no -l with bzip2 files");
1436			goto lose;
1437		}
1438
1439		size = unbzip2(fd, zfd, NULL, 0, NULL);
1440	} else
1441#endif
1442
1443#ifndef NO_COMPRESS_SUPPORT
1444	if (method == FT_Z) {
1445		FILE *in, *out;
1446
1447		/* XXX */
1448		if (lflag) {
1449			maybe_warnx("no -l with Lempel-Ziv files");
1450			goto lose;
1451		}
1452
1453		if ((in = zdopen(fd)) == NULL) {
1454			maybe_warn("zdopen for read: %s", file);
1455			goto lose;
1456		}
1457
1458		out = fdopen(dup(zfd), "w");
1459		if (out == NULL) {
1460			maybe_warn("fdopen for write: %s", outfile);
1461			fclose(in);
1462			goto lose;
1463		}
1464
1465		size = zuncompress(in, out, NULL, 0, NULL);
1466		/* need to fclose() if ferror() is true... */
1467		if (ferror(in) | fclose(in)) {
1468			maybe_warn("failed infile fclose");
1469			unlink(outfile);
1470			(void)fclose(out);
1471		}
1472		if (fclose(out) != 0) {
1473			maybe_warn("failed outfile fclose");
1474			unlink(outfile);
1475			goto lose;
1476		}
1477	} else
1478#endif
1479
1480#ifndef NO_PACK_SUPPORT
1481	if (method == FT_PACK) {
1482		if (lflag) {
1483			maybe_warnx("no -l with packed files");
1484			goto lose;
1485		}
1486
1487		size = unpack(fd, zfd, NULL, 0, NULL);
1488	} else
1489#endif
1490
1491#ifndef SMALL
1492	if (method == FT_UNKNOWN) {
1493		if (lflag) {
1494			maybe_warnx("no -l for unknown filetypes");
1495			goto lose;
1496		}
1497		size = cat_fd(NULL, 0, NULL, fd);
1498	} else
1499#endif
1500	{
1501		if (lflag) {
1502			print_list(fd, isb.st_size, outfile, isb.st_mtime);
1503			close(fd);
1504			return -1;	/* XXX */
1505		}
1506
1507		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1508	}
1509
1510	if (close(fd) != 0)
1511		maybe_warn("couldn't close input");
1512	if (zfd != STDOUT_FILENO && close(zfd) != 0)
1513		maybe_warn("couldn't close output");
1514
1515	if (size == -1) {
1516		if (cflag == 0)
1517			unlink(outfile);
1518		maybe_warnx("%s: uncompress failed", file);
1519		return -1;
1520	}
1521
1522	/* if testing, or we uncompressed to stdout, this is all we need */
1523#ifndef SMALL
1524	if (tflag)
1525		return size;
1526#endif
1527	/* if we are uncompressing to stdin, don't remove the file. */
1528	if (cflag)
1529		return size;
1530
1531	/*
1532	 * if we create a file...
1533	 */
1534	/*
1535	 * if we can't stat the file don't remove the file.
1536	 */
1537
1538	ofd = open(outfile, O_RDWR, 0);
1539	if (ofd == -1) {
1540		maybe_warn("couldn't open (leaving original): %s",
1541			   outfile);
1542		return -1;
1543	}
1544	if (fstat(ofd, &osb) != 0) {
1545		maybe_warn("couldn't stat (leaving original): %s",
1546			   outfile);
1547		close(ofd);
1548		return -1;
1549	}
1550	if (osb.st_size != size) {
1551		maybe_warnx("stat gave different size: %" PRIdOFF
1552				" != %" PRIdOFF " (leaving original)",
1553				size, osb.st_size);
1554		close(ofd);
1555		unlink(outfile);
1556		return -1;
1557	}
1558	unlink_input(file, &isb);
1559#ifndef SMALL
1560	copymodes(ofd, &isb, outfile);
1561#endif
1562	close(ofd);
1563	return size;
1564
1565    unexpected_EOF:
1566	maybe_warnx("%s: unexpected end of file", file);
1567    lose:
1568	if (fd != -1)
1569		close(fd);
1570	if (zfd != -1 && zfd != STDOUT_FILENO)
1571		close(fd);
1572	return -1;
1573}
1574
1575#ifndef SMALL
1576static off_t
1577cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1578{
1579	char buf[BUFLEN];
1580	off_t in_tot;
1581	ssize_t w;
1582
1583	in_tot = count;
1584	w = write(STDOUT_FILENO, prepend, count);
1585	if (w == -1 || (size_t)w != count) {
1586		maybe_warn("write to stdout");
1587		return -1;
1588	}
1589	for (;;) {
1590		ssize_t rv;
1591
1592		rv = read(fd, buf, sizeof buf);
1593		if (rv == 0)
1594			break;
1595		if (rv < 0) {
1596			maybe_warn("read from fd %d", fd);
1597			break;
1598		}
1599
1600		if (write(STDOUT_FILENO, buf, rv) != rv) {
1601			maybe_warn("write to stdout");
1602			break;
1603		}
1604		in_tot += rv;
1605	}
1606
1607	if (gsizep)
1608		*gsizep = in_tot;
1609	return (in_tot);
1610}
1611#endif
1612
1613static void
1614handle_stdin(void)
1615{
1616	unsigned char header1[4];
1617	off_t usize, gsize;
1618	enum filetype method;
1619	ssize_t bytes_read;
1620#ifndef NO_COMPRESS_SUPPORT
1621	FILE *in;
1622#endif
1623
1624#ifndef SMALL
1625	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1626		maybe_warnx("standard input is a terminal -- ignoring");
1627		return;
1628	}
1629#endif
1630
1631	if (lflag) {
1632		struct stat isb;
1633
1634		/* XXX could read the whole file, etc. */
1635		if (fstat(STDIN_FILENO, &isb) < 0) {
1636			maybe_warn("fstat");
1637			return;
1638		}
1639		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1640		return;
1641	}
1642
1643	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1644	if (bytes_read == -1) {
1645		maybe_warn("can't read stdin");
1646		return;
1647	} else if (bytes_read != sizeof(header1)) {
1648		maybe_warnx("(stdin): unexpected end of file");
1649		return;
1650	}
1651
1652	method = file_gettype(header1);
1653	switch (method) {
1654	default:
1655#ifndef SMALL
1656		if (fflag == 0) {
1657			maybe_warnx("unknown compression format");
1658			return;
1659		}
1660		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1661		break;
1662#endif
1663	case FT_GZIP:
1664		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1665			      (char *)header1, sizeof header1, &gsize, "(stdin)");
1666		break;
1667#ifndef NO_BZIP2_SUPPORT
1668	case FT_BZIP2:
1669		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1670				(char *)header1, sizeof header1, &gsize);
1671		break;
1672#endif
1673#ifndef NO_COMPRESS_SUPPORT
1674	case FT_Z:
1675		if ((in = zdopen(STDIN_FILENO)) == NULL) {
1676			maybe_warnx("zopen of stdin");
1677			return;
1678		}
1679
1680		usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize);
1681		fclose(in);
1682		break;
1683#endif
1684#ifndef NO_PACK_SUPPORT
1685	case FT_PACK:
1686		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1687			       (char *)header1, sizeof header1, &gsize);
1688		break;
1689#endif
1690	}
1691
1692#ifndef SMALL
1693        if (vflag && !tflag && usize != -1 && gsize != -1)
1694		print_verbage(NULL, NULL, usize, gsize);
1695	if (vflag && tflag)
1696		print_test("(stdin)", usize != -1);
1697#endif
1698
1699}
1700
1701static void
1702handle_stdout(void)
1703{
1704	off_t gsize, usize;
1705	struct stat sb;
1706	time_t systime;
1707	uint32_t mtime;
1708	int ret;
1709
1710#ifndef SMALL
1711	if (fflag == 0 && isatty(STDOUT_FILENO)) {
1712		maybe_warnx("standard output is a terminal -- ignoring");
1713		return;
1714	}
1715#endif
1716	/* If stdin is a file use it's mtime, otherwise use current time */
1717	ret = fstat(STDIN_FILENO, &sb);
1718
1719#ifndef SMALL
1720	if (ret < 0) {
1721		maybe_warn("Can't stat stdin");
1722		return;
1723	}
1724#endif
1725
1726	if (S_ISREG(sb.st_mode))
1727		mtime = (uint32_t)sb.st_mtime;
1728	else {
1729		systime = time(NULL);
1730#ifndef SMALL
1731		if (systime == -1) {
1732			maybe_warn("time");
1733			return;
1734		}
1735#endif
1736		mtime = (uint32_t)systime;
1737	}
1738
1739	usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1740#ifndef SMALL
1741        if (vflag && !tflag && usize != -1 && gsize != -1)
1742		print_verbage(NULL, NULL, usize, gsize);
1743#endif
1744}
1745
1746/* do what is asked for, for the path name */
1747static void
1748handle_pathname(char *path)
1749{
1750	char *opath = path, *s = NULL;
1751	ssize_t len;
1752	int slen;
1753	struct stat sb;
1754
1755	/* check for stdout/stdin */
1756	if (path[0] == '-' && path[1] == '\0') {
1757		if (dflag)
1758			handle_stdin();
1759		else
1760			handle_stdout();
1761		return;
1762	}
1763
1764retry:
1765	if (stat(path, &sb) != 0) {
1766		/* lets try <path>.gz if we're decompressing */
1767		if (dflag && s == NULL && errno == ENOENT) {
1768			len = strlen(path);
1769			slen = suffixes[0].ziplen;
1770			s = malloc(len + slen + 1);
1771			if (s == NULL)
1772				maybe_err("malloc");
1773			memcpy(s, path, len);
1774			memcpy(s + len, suffixes[0].zipped, slen + 1);
1775			path = s;
1776			goto retry;
1777		}
1778		maybe_warn("can't stat: %s", opath);
1779		goto out;
1780	}
1781
1782	if (S_ISDIR(sb.st_mode)) {
1783#ifndef SMALL
1784		if (rflag)
1785			handle_dir(path);
1786		else
1787#endif
1788			maybe_warnx("%s is a directory", path);
1789		goto out;
1790	}
1791
1792	if (S_ISREG(sb.st_mode))
1793		handle_file(path, &sb);
1794	else
1795		maybe_warnx("%s is not a regular file", path);
1796
1797out:
1798	if (s)
1799		free(s);
1800}
1801
1802/* compress/decompress a file */
1803static void
1804handle_file(char *file, struct stat *sbp)
1805{
1806	off_t usize, gsize;
1807	char	outfile[PATH_MAX];
1808
1809	infile = file;
1810	if (dflag) {
1811		usize = file_uncompress(file, outfile, sizeof(outfile));
1812#ifndef SMALL
1813		if (vflag && tflag)
1814			print_test(file, usize != -1);
1815#endif
1816		if (usize == -1)
1817			return;
1818		gsize = sbp->st_size;
1819	} else {
1820		gsize = file_compress(file, outfile, sizeof(outfile));
1821		if (gsize == -1)
1822			return;
1823		usize = sbp->st_size;
1824	}
1825
1826
1827#ifndef SMALL
1828	if (vflag && !tflag)
1829		print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1830#endif
1831}
1832
1833#ifndef SMALL
1834/* this is used with -r to recursively descend directories */
1835static void
1836handle_dir(char *dir)
1837{
1838	char *path_argv[2];
1839	FTS *fts;
1840	FTSENT *entry;
1841
1842	path_argv[0] = dir;
1843	path_argv[1] = 0;
1844	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1845	if (fts == NULL) {
1846		warn("couldn't fts_open %s", dir);
1847		return;
1848	}
1849
1850	while ((entry = fts_read(fts))) {
1851		switch(entry->fts_info) {
1852		case FTS_D:
1853		case FTS_DP:
1854			continue;
1855
1856		case FTS_DNR:
1857		case FTS_ERR:
1858		case FTS_NS:
1859			maybe_warn("%s", entry->fts_path);
1860			continue;
1861		case FTS_F:
1862			handle_file(entry->fts_path, entry->fts_statp);
1863		}
1864	}
1865	(void)fts_close(fts);
1866}
1867#endif
1868
1869/* print a ratio - size reduction as a fraction of uncompressed size */
1870static void
1871print_ratio(off_t in, off_t out, FILE *where)
1872{
1873	int percent10;	/* 10 * percent */
1874	off_t diff;
1875	char buff[8];
1876	int len;
1877
1878	diff = in - out/2;
1879	if (diff <= 0)
1880		/*
1881		 * Output is more than double size of input! print -99.9%
1882		 * Quite possibly we've failed to get the original size.
1883		 */
1884		percent10 = -999;
1885	else {
1886		/*
1887		 * We only need 12 bits of result from the final division,
1888		 * so reduce the values until a 32bit division will suffice.
1889		 */
1890		while (in > 0x100000) {
1891			diff >>= 1;
1892			in >>= 1;
1893		}
1894		if (in != 0)
1895			percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1896		else
1897			percent10 = 0;
1898	}
1899
1900	len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1901	/* Move the '.' to before the last digit */
1902	buff[len - 1] = buff[len - 2];
1903	buff[len - 2] = '.';
1904	fprintf(where, "%5s%%", buff);
1905}
1906
1907#ifndef SMALL
1908/* print compression statistics, and the new name (if there is one!) */
1909static void
1910print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1911{
1912	if (file)
1913		fprintf(stderr, "%s:%s  ", file,
1914		    strlen(file) < 7 ? "\t\t" : "\t");
1915	print_ratio(usize, gsize, stderr);
1916	if (nfile)
1917		fprintf(stderr, " -- replaced with %s", nfile);
1918	fprintf(stderr, "\n");
1919	fflush(stderr);
1920}
1921
1922/* print test results */
1923static void
1924print_test(const char *file, int ok)
1925{
1926
1927	if (exit_value == 0 && ok == 0)
1928		exit_value = 1;
1929	fprintf(stderr, "%s:%s  %s\n", file,
1930	    strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1931	fflush(stderr);
1932}
1933#endif
1934
1935/* print a file's info ala --list */
1936/* eg:
1937  compressed uncompressed  ratio uncompressed_name
1938      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1939*/
1940static void
1941print_list(int fd, off_t out, const char *outfile, time_t ts)
1942{
1943	static int first = 1;
1944#ifndef SMALL
1945	static off_t in_tot, out_tot;
1946	uint32_t crc = 0;
1947#endif
1948	off_t in = 0, rv;
1949
1950	if (first) {
1951#ifndef SMALL
1952		if (vflag)
1953			printf("method  crc     date  time  ");
1954#endif
1955		if (qflag == 0)
1956			printf("  compressed uncompressed  "
1957			       "ratio uncompressed_name\n");
1958	}
1959	first = 0;
1960
1961	/* print totals? */
1962#ifndef SMALL
1963	if (fd == -1) {
1964		in = in_tot;
1965		out = out_tot;
1966	} else
1967#endif
1968	{
1969		/* read the last 4 bytes - this is the uncompressed size */
1970		rv = lseek(fd, (off_t)(-8), SEEK_END);
1971		if (rv != -1) {
1972			unsigned char buf[8];
1973			uint32_t usize;
1974
1975			rv = read(fd, (char *)buf, sizeof(buf));
1976			if (rv == -1)
1977				maybe_warn("read of uncompressed size");
1978			else if (rv != sizeof(buf))
1979				maybe_warnx("read of uncompressed size");
1980
1981			else {
1982				usize = buf[4] | buf[5] << 8 |
1983					buf[6] << 16 | buf[7] << 24;
1984				in = (off_t)usize;
1985#ifndef SMALL
1986				crc = buf[0] | buf[1] << 8 |
1987				      buf[2] << 16 | buf[3] << 24;
1988#endif
1989			}
1990		}
1991	}
1992
1993#ifndef SMALL
1994	if (vflag && fd == -1)
1995		printf("                            ");
1996	else if (vflag) {
1997		char *date = ctime(&ts);
1998
1999		/* skip the day, 1/100th second, and year */
2000		date += 4;
2001		date[12] = 0;
2002		printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2003	}
2004	in_tot += in;
2005	out_tot += out;
2006#else
2007	(void)&ts;	/* XXX */
2008#endif
2009	printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2010	print_ratio(in, out, stdout);
2011	printf(" %s\n", outfile);
2012}
2013
2014/* display the usage of NetBSD gzip */
2015static void
2016usage(void)
2017{
2018
2019	fprintf(stderr, "%s\n", gzip_version);
2020	fprintf(stderr,
2021#ifdef SMALL
2022    "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2023#else
2024    "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2025    " -1 --fast            fastest (worst) compression\n"
2026    " -2 .. -8             set compression level\n"
2027    " -9 --best            best (slowest) compression\n"
2028    " -c --stdout          write to stdout, keep original files\n"
2029    "    --to-stdout\n"
2030    " -d --decompress      uncompress files\n"
2031    "    --uncompress\n"
2032    " -f --force           force overwriting & compress links\n"
2033    " -h --help            display this help\n"
2034    " -k --keep            don't delete input files during operation\n"
2035    " -l --list            list compressed file contents\n"
2036    " -N --name            save or restore original file name and time stamp\n"
2037    " -n --no-name         don't save original file name or time stamp\n"
2038    " -q --quiet           output no warnings\n"
2039    " -r --recursive       recursively compress files in directories\n"
2040    " -S .suf              use suffix .suf instead of .gz\n"
2041    "    --suffix .suf\n"
2042    " -t --test            test compressed file\n"
2043    " -V --version         display program version\n"
2044    " -v --verbose         print extra statistics\n",
2045#endif
2046	    getprogname());
2047	exit(0);
2048}
2049
2050#ifndef SMALL
2051/* display the license information of FreeBSD gzip */
2052static void
2053display_license(void)
2054{
2055
2056	fprintf(stderr, "%s (based on NetBSD gzip 20091011)\n", gzip_version);
2057	fprintf(stderr, "%s\n", gzip_copyright);
2058	exit(0);
2059}
2060#endif
2061
2062/* display the version of NetBSD gzip */
2063static void
2064display_version(void)
2065{
2066
2067	fprintf(stderr, "%s\n", gzip_version);
2068	exit(0);
2069}
2070
2071#ifndef NO_BZIP2_SUPPORT
2072#include "unbzip2.c"
2073#endif
2074#ifndef NO_COMPRESS_SUPPORT
2075#include "zuncompress.c"
2076#endif
2077#ifndef NO_PACK_SUPPORT
2078#include "unpack.c"
2079#endif
2080
2081static ssize_t
2082read_retry(int fd, void *buf, size_t sz)
2083{
2084	char *cp = buf;
2085	size_t left = MIN(sz, (size_t) SSIZE_MAX);
2086
2087	while (left > 0) {
2088		ssize_t ret;
2089
2090		ret = read(fd, cp, left);
2091		if (ret == -1) {
2092			return ret;
2093		} else if (ret == 0) {
2094			break; /* EOF */
2095		}
2096		cp += ret;
2097		left -= ret;
2098	}
2099
2100	return sz - left;
2101}
2102