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