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