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