xref: /freebsd/contrib/file/src/compress.c (revision 4460e5b02d2e07d8aa286fff8f644a442b376cae)
1b6cee71dSXin LI /*
2b6cee71dSXin LI  * Copyright (c) Ian F. Darwin 1986-1995.
3b6cee71dSXin LI  * Software written by Ian F. Darwin and others;
4b6cee71dSXin LI  * maintained 1995-present by Christos Zoulas and others.
5b6cee71dSXin LI  *
6b6cee71dSXin LI  * Redistribution and use in source and binary forms, with or without
7b6cee71dSXin LI  * modification, are permitted provided that the following conditions
8b6cee71dSXin LI  * are met:
9b6cee71dSXin LI  * 1. Redistributions of source code must retain the above copyright
10b6cee71dSXin LI  *    notice immediately at the beginning of the file, without modification,
11b6cee71dSXin LI  *    this list of conditions, and the following disclaimer.
12b6cee71dSXin LI  * 2. Redistributions in binary form must reproduce the above copyright
13b6cee71dSXin LI  *    notice, this list of conditions and the following disclaimer in the
14b6cee71dSXin LI  *    documentation and/or other materials provided with the distribution.
15b6cee71dSXin LI  *
16b6cee71dSXin LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17b6cee71dSXin LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b6cee71dSXin LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b6cee71dSXin LI  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20b6cee71dSXin LI  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21b6cee71dSXin LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22b6cee71dSXin LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b6cee71dSXin LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24b6cee71dSXin LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25b6cee71dSXin LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26b6cee71dSXin LI  * SUCH DAMAGE.
27b6cee71dSXin LI  */
28b6cee71dSXin LI /*
29b6cee71dSXin LI  * compress routines:
30b6cee71dSXin LI  *	zmagic() - returns 0 if not recognized, uncompresses and prints
31b6cee71dSXin LI  *		   information if recognized
32b6cee71dSXin LI  *	uncompress(method, old, n, newch) - uncompress old into new,
33b6cee71dSXin LI  *					    using method, return sizeof new
34b6cee71dSXin LI  */
35b6cee71dSXin LI #include "file.h"
36b6cee71dSXin LI 
37b6cee71dSXin LI #ifndef lint
38*4460e5b0SXin LI FILE_RCSID("@(#)$File: compress.c,v 1.77 2014/12/12 16:33:01 christos Exp $")
39b6cee71dSXin LI #endif
40b6cee71dSXin LI 
41b6cee71dSXin LI #include "magic.h"
42b6cee71dSXin LI #include <stdlib.h>
43b6cee71dSXin LI #ifdef HAVE_UNISTD_H
44b6cee71dSXin LI #include <unistd.h>
45b6cee71dSXin LI #endif
46b6cee71dSXin LI #include <string.h>
47b6cee71dSXin LI #include <errno.h>
48*4460e5b0SXin LI #include <signal.h>
49c2931133SXin LI #if !defined(__MINGW32__) && !defined(WIN32)
50b6cee71dSXin LI #include <sys/ioctl.h>
51b6cee71dSXin LI #endif
52b6cee71dSXin LI #ifdef HAVE_SYS_WAIT_H
53b6cee71dSXin LI #include <sys/wait.h>
54b6cee71dSXin LI #endif
55b6cee71dSXin LI #if defined(HAVE_SYS_TIME_H)
56b6cee71dSXin LI #include <sys/time.h>
57b6cee71dSXin LI #endif
58b6cee71dSXin LI #if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
59b6cee71dSXin LI #define BUILTIN_DECOMPRESS
60b6cee71dSXin LI #include <zlib.h>
61b6cee71dSXin LI #endif
62b6cee71dSXin LI 
63b6cee71dSXin LI private const struct {
64b6cee71dSXin LI 	const char magic[8];
65b6cee71dSXin LI 	size_t maglen;
66b6cee71dSXin LI 	const char *argv[3];
67b6cee71dSXin LI 	int silent;
68b6cee71dSXin LI } compr[] = {
69b6cee71dSXin LI 	{ "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },		/* compressed */
70b6cee71dSXin LI 	/* Uncompress can get stuck; so use gzip first if we have it
71b6cee71dSXin LI 	 * Idea from Damien Clark, thanks! */
72b6cee71dSXin LI 	{ "\037\235", 2, { "uncompress", "-c", NULL }, 1 },	/* compressed */
73b6cee71dSXin LI 	{ "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },		/* gzipped */
74b6cee71dSXin LI 	{ "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },		/* frozen */
75b6cee71dSXin LI 	{ "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },		/* SCO LZH */
76b6cee71dSXin LI 	/* the standard pack utilities do not accept standard input */
77b6cee71dSXin LI 	{ "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },		/* packed */
78b6cee71dSXin LI 	{ "PK\3\4",   4, { "gzip", "-cdq", NULL }, 1 },		/* pkzipped, */
79b6cee71dSXin LI 					    /* ...only first file examined */
80b6cee71dSXin LI 	{ "BZh",      3, { "bzip2", "-cd", NULL }, 1 },		/* bzip2-ed */
81b6cee71dSXin LI 	{ "LZIP",     4, { "lzip", "-cdq", NULL }, 1 },
82b6cee71dSXin LI  	{ "\3757zXZ\0",6,{ "xz", "-cd", NULL }, 1 },		/* XZ Utils */
83b6cee71dSXin LI  	{ "LRZI",     4, { "lrzip", "-dqo-", NULL }, 1 },	/* LRZIP */
84b6cee71dSXin LI  	{ "\004\"M\030", 4, { "lz4", "-cd", NULL }, 1 },	/* LZ4 */
85b6cee71dSXin LI };
86b6cee71dSXin LI 
87b6cee71dSXin LI #define NODATA ((size_t)~0)
88b6cee71dSXin LI 
89b6cee71dSXin LI private ssize_t swrite(int, const void *, size_t);
90b6cee71dSXin LI #if HAVE_FORK
91b6cee71dSXin LI private size_t ncompr = sizeof(compr) / sizeof(compr[0]);
92b6cee71dSXin LI private size_t uncompressbuf(struct magic_set *, int, size_t,
93b6cee71dSXin LI     const unsigned char *, unsigned char **, size_t);
94b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
95b6cee71dSXin LI private size_t uncompressgzipped(struct magic_set *, const unsigned char *,
96b6cee71dSXin LI     unsigned char **, size_t);
97b6cee71dSXin LI #endif
98b6cee71dSXin LI 
99b6cee71dSXin LI protected int
100b6cee71dSXin LI file_zmagic(struct magic_set *ms, int fd, const char *name,
101b6cee71dSXin LI     const unsigned char *buf, size_t nbytes)
102b6cee71dSXin LI {
103b6cee71dSXin LI 	unsigned char *newbuf = NULL;
104b6cee71dSXin LI 	size_t i, nsz;
105b6cee71dSXin LI 	int rv = 0;
106b6cee71dSXin LI 	int mime = ms->flags & MAGIC_MIME;
107*4460e5b0SXin LI 	sig_t osigpipe;
108b6cee71dSXin LI 
109b6cee71dSXin LI 	if ((ms->flags & MAGIC_COMPRESS) == 0)
110b6cee71dSXin LI 		return 0;
111b6cee71dSXin LI 
112*4460e5b0SXin LI 	osigpipe = signal(SIGPIPE, SIG_IGN);
113b6cee71dSXin LI 	for (i = 0; i < ncompr; i++) {
114b6cee71dSXin LI 		if (nbytes < compr[i].maglen)
115b6cee71dSXin LI 			continue;
116b6cee71dSXin LI 		if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
117b6cee71dSXin LI 		    (nsz = uncompressbuf(ms, fd, i, buf, &newbuf,
118b6cee71dSXin LI 		    nbytes)) != NODATA) {
119b6cee71dSXin LI 			ms->flags &= ~MAGIC_COMPRESS;
120b6cee71dSXin LI 			rv = -1;
121b6cee71dSXin LI 			if (file_buffer(ms, -1, name, newbuf, nsz) == -1)
122b6cee71dSXin LI 				goto error;
123b6cee71dSXin LI 
124b6cee71dSXin LI 			if (mime == MAGIC_MIME || mime == 0) {
125b6cee71dSXin LI 				if (file_printf(ms, mime ?
126b6cee71dSXin LI 				    " compressed-encoding=" : " (") == -1)
127b6cee71dSXin LI 					goto error;
128b6cee71dSXin LI 				if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
129b6cee71dSXin LI 					goto error;
130b6cee71dSXin LI 				if (!mime && file_printf(ms, ")") == -1)
131b6cee71dSXin LI 					goto error;
132b6cee71dSXin LI 			}
133b6cee71dSXin LI 
134b6cee71dSXin LI 			rv = 1;
135b6cee71dSXin LI 			break;
136b6cee71dSXin LI 		}
137b6cee71dSXin LI 	}
138b6cee71dSXin LI error:
139*4460e5b0SXin LI 	(void)signal(SIGPIPE, osigpipe);
140b6cee71dSXin LI 	free(newbuf);
141b6cee71dSXin LI 	ms->flags |= MAGIC_COMPRESS;
142b6cee71dSXin LI 	return rv;
143b6cee71dSXin LI }
144b6cee71dSXin LI #endif
145b6cee71dSXin LI /*
146b6cee71dSXin LI  * `safe' write for sockets and pipes.
147b6cee71dSXin LI  */
148b6cee71dSXin LI private ssize_t
149b6cee71dSXin LI swrite(int fd, const void *buf, size_t n)
150b6cee71dSXin LI {
151b6cee71dSXin LI 	ssize_t rv;
152b6cee71dSXin LI 	size_t rn = n;
153b6cee71dSXin LI 
154b6cee71dSXin LI 	do
155b6cee71dSXin LI 		switch (rv = write(fd, buf, n)) {
156b6cee71dSXin LI 		case -1:
157b6cee71dSXin LI 			if (errno == EINTR)
158b6cee71dSXin LI 				continue;
159b6cee71dSXin LI 			return -1;
160b6cee71dSXin LI 		default:
161b6cee71dSXin LI 			n -= rv;
162b6cee71dSXin LI 			buf = CAST(const char *, buf) + rv;
163b6cee71dSXin LI 			break;
164b6cee71dSXin LI 		}
165b6cee71dSXin LI 	while (n > 0);
166b6cee71dSXin LI 	return rn;
167b6cee71dSXin LI }
168b6cee71dSXin LI 
169b6cee71dSXin LI 
170b6cee71dSXin LI /*
171b6cee71dSXin LI  * `safe' read for sockets and pipes.
172b6cee71dSXin LI  */
173b6cee71dSXin LI protected ssize_t
174b6cee71dSXin LI sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
175b6cee71dSXin LI {
176b6cee71dSXin LI 	ssize_t rv;
177b6cee71dSXin LI #ifdef FIONREAD
178b6cee71dSXin LI 	int t = 0;
179b6cee71dSXin LI #endif
180b6cee71dSXin LI 	size_t rn = n;
181b6cee71dSXin LI 
182b6cee71dSXin LI 	if (fd == STDIN_FILENO)
183b6cee71dSXin LI 		goto nocheck;
184b6cee71dSXin LI 
185b6cee71dSXin LI #ifdef FIONREAD
186b6cee71dSXin LI 	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
187b6cee71dSXin LI #ifdef FD_ZERO
188b6cee71dSXin LI 		ssize_t cnt;
189b6cee71dSXin LI 		for (cnt = 0;; cnt++) {
190b6cee71dSXin LI 			fd_set check;
191b6cee71dSXin LI 			struct timeval tout = {0, 100 * 1000};
192b6cee71dSXin LI 			int selrv;
193b6cee71dSXin LI 
194b6cee71dSXin LI 			FD_ZERO(&check);
195b6cee71dSXin LI 			FD_SET(fd, &check);
196b6cee71dSXin LI 
197b6cee71dSXin LI 			/*
198b6cee71dSXin LI 			 * Avoid soft deadlock: do not read if there
199b6cee71dSXin LI 			 * is nothing to read from sockets and pipes.
200b6cee71dSXin LI 			 */
201b6cee71dSXin LI 			selrv = select(fd + 1, &check, NULL, NULL, &tout);
202b6cee71dSXin LI 			if (selrv == -1) {
203b6cee71dSXin LI 				if (errno == EINTR || errno == EAGAIN)
204b6cee71dSXin LI 					continue;
205b6cee71dSXin LI 			} else if (selrv == 0 && cnt >= 5) {
206b6cee71dSXin LI 				return 0;
207b6cee71dSXin LI 			} else
208b6cee71dSXin LI 				break;
209b6cee71dSXin LI 		}
210b6cee71dSXin LI #endif
211b6cee71dSXin LI 		(void)ioctl(fd, FIONREAD, &t);
212b6cee71dSXin LI 	}
213b6cee71dSXin LI 
214b6cee71dSXin LI 	if (t > 0 && (size_t)t < n) {
215b6cee71dSXin LI 		n = t;
216b6cee71dSXin LI 		rn = n;
217b6cee71dSXin LI 	}
218b6cee71dSXin LI #endif
219b6cee71dSXin LI 
220b6cee71dSXin LI nocheck:
221b6cee71dSXin LI 	do
222b6cee71dSXin LI 		switch ((rv = read(fd, buf, n))) {
223b6cee71dSXin LI 		case -1:
224b6cee71dSXin LI 			if (errno == EINTR)
225b6cee71dSXin LI 				continue;
226b6cee71dSXin LI 			return -1;
227b6cee71dSXin LI 		case 0:
228b6cee71dSXin LI 			return rn - n;
229b6cee71dSXin LI 		default:
230b6cee71dSXin LI 			n -= rv;
231b6cee71dSXin LI 			buf = ((char *)buf) + rv;
232b6cee71dSXin LI 			break;
233b6cee71dSXin LI 		}
234b6cee71dSXin LI 	while (n > 0);
235b6cee71dSXin LI 	return rn;
236b6cee71dSXin LI }
237b6cee71dSXin LI 
238b6cee71dSXin LI protected int
239b6cee71dSXin LI file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
240b6cee71dSXin LI     size_t nbytes)
241b6cee71dSXin LI {
242b6cee71dSXin LI 	char buf[4096];
243b6cee71dSXin LI 	ssize_t r;
244b6cee71dSXin LI 	int tfd;
245b6cee71dSXin LI 
246b6cee71dSXin LI 	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
247b6cee71dSXin LI #ifndef HAVE_MKSTEMP
248b6cee71dSXin LI 	{
249b6cee71dSXin LI 		char *ptr = mktemp(buf);
250b6cee71dSXin LI 		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
251b6cee71dSXin LI 		r = errno;
252b6cee71dSXin LI 		(void)unlink(ptr);
253b6cee71dSXin LI 		errno = r;
254b6cee71dSXin LI 	}
255b6cee71dSXin LI #else
256b6cee71dSXin LI 	{
257b6cee71dSXin LI 		int te;
258b6cee71dSXin LI 		tfd = mkstemp(buf);
259b6cee71dSXin LI 		te = errno;
260b6cee71dSXin LI 		(void)unlink(buf);
261b6cee71dSXin LI 		errno = te;
262b6cee71dSXin LI 	}
263b6cee71dSXin LI #endif
264b6cee71dSXin LI 	if (tfd == -1) {
265b6cee71dSXin LI 		file_error(ms, errno,
266b6cee71dSXin LI 		    "cannot create temporary file for pipe copy");
267b6cee71dSXin LI 		return -1;
268b6cee71dSXin LI 	}
269b6cee71dSXin LI 
270b6cee71dSXin LI 	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
271b6cee71dSXin LI 		r = 1;
272b6cee71dSXin LI 	else {
273b6cee71dSXin LI 		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
274b6cee71dSXin LI 			if (swrite(tfd, buf, (size_t)r) != r)
275b6cee71dSXin LI 				break;
276b6cee71dSXin LI 	}
277b6cee71dSXin LI 
278b6cee71dSXin LI 	switch (r) {
279b6cee71dSXin LI 	case -1:
280b6cee71dSXin LI 		file_error(ms, errno, "error copying from pipe to temp file");
281b6cee71dSXin LI 		return -1;
282b6cee71dSXin LI 	case 0:
283b6cee71dSXin LI 		break;
284b6cee71dSXin LI 	default:
285b6cee71dSXin LI 		file_error(ms, errno, "error while writing to temp file");
286b6cee71dSXin LI 		return -1;
287b6cee71dSXin LI 	}
288b6cee71dSXin LI 
289b6cee71dSXin LI 	/*
290b6cee71dSXin LI 	 * We duplicate the file descriptor, because fclose on a
291b6cee71dSXin LI 	 * tmpfile will delete the file, but any open descriptors
292b6cee71dSXin LI 	 * can still access the phantom inode.
293b6cee71dSXin LI 	 */
294b6cee71dSXin LI 	if ((fd = dup2(tfd, fd)) == -1) {
295b6cee71dSXin LI 		file_error(ms, errno, "could not dup descriptor for temp file");
296b6cee71dSXin LI 		return -1;
297b6cee71dSXin LI 	}
298b6cee71dSXin LI 	(void)close(tfd);
299b6cee71dSXin LI 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
300b6cee71dSXin LI 		file_badseek(ms);
301b6cee71dSXin LI 		return -1;
302b6cee71dSXin LI 	}
303b6cee71dSXin LI 	return fd;
304b6cee71dSXin LI }
305b6cee71dSXin LI #if HAVE_FORK
306b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
307b6cee71dSXin LI 
308b6cee71dSXin LI #define FHCRC		(1 << 1)
309b6cee71dSXin LI #define FEXTRA		(1 << 2)
310b6cee71dSXin LI #define FNAME		(1 << 3)
311b6cee71dSXin LI #define FCOMMENT	(1 << 4)
312b6cee71dSXin LI 
313b6cee71dSXin LI private size_t
314b6cee71dSXin LI uncompressgzipped(struct magic_set *ms, const unsigned char *old,
315b6cee71dSXin LI     unsigned char **newch, size_t n)
316b6cee71dSXin LI {
317b6cee71dSXin LI 	unsigned char flg = old[3];
318b6cee71dSXin LI 	size_t data_start = 10;
319b6cee71dSXin LI 	z_stream z;
320b6cee71dSXin LI 	int rc;
321b6cee71dSXin LI 
322b6cee71dSXin LI 	if (flg & FEXTRA) {
323b6cee71dSXin LI 		if (data_start+1 >= n)
324b6cee71dSXin LI 			return 0;
325b6cee71dSXin LI 		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
326b6cee71dSXin LI 	}
327b6cee71dSXin LI 	if (flg & FNAME) {
328b6cee71dSXin LI 		while(data_start < n && old[data_start])
329b6cee71dSXin LI 			data_start++;
330b6cee71dSXin LI 		data_start++;
331b6cee71dSXin LI 	}
332b6cee71dSXin LI 	if(flg & FCOMMENT) {
333b6cee71dSXin LI 		while(data_start < n && old[data_start])
334b6cee71dSXin LI 			data_start++;
335b6cee71dSXin LI 		data_start++;
336b6cee71dSXin LI 	}
337b6cee71dSXin LI 	if(flg & FHCRC)
338b6cee71dSXin LI 		data_start += 2;
339b6cee71dSXin LI 
340b6cee71dSXin LI 	if (data_start >= n)
341b6cee71dSXin LI 		return 0;
342b6cee71dSXin LI 	if ((*newch = CAST(unsigned char *, malloc(HOWMANY + 1))) == NULL) {
343b6cee71dSXin LI 		return 0;
344b6cee71dSXin LI 	}
345b6cee71dSXin LI 
346b6cee71dSXin LI 	/* XXX: const castaway, via strchr */
347b6cee71dSXin LI 	z.next_in = (Bytef *)strchr((const char *)old + data_start,
348b6cee71dSXin LI 	    old[data_start]);
349b6cee71dSXin LI 	z.avail_in = CAST(uint32_t, (n - data_start));
350b6cee71dSXin LI 	z.next_out = *newch;
351b6cee71dSXin LI 	z.avail_out = HOWMANY;
352b6cee71dSXin LI 	z.zalloc = Z_NULL;
353b6cee71dSXin LI 	z.zfree = Z_NULL;
354b6cee71dSXin LI 	z.opaque = Z_NULL;
355b6cee71dSXin LI 
356b6cee71dSXin LI 	/* LINTED bug in header macro */
357b6cee71dSXin LI 	rc = inflateInit2(&z, -15);
358b6cee71dSXin LI 	if (rc != Z_OK) {
359b6cee71dSXin LI 		file_error(ms, 0, "zlib: %s", z.msg);
360b6cee71dSXin LI 		return 0;
361b6cee71dSXin LI 	}
362b6cee71dSXin LI 
363b6cee71dSXin LI 	rc = inflate(&z, Z_SYNC_FLUSH);
364b6cee71dSXin LI 	if (rc != Z_OK && rc != Z_STREAM_END) {
365b6cee71dSXin LI 		file_error(ms, 0, "zlib: %s", z.msg);
366b6cee71dSXin LI 		return 0;
367b6cee71dSXin LI 	}
368b6cee71dSXin LI 
369b6cee71dSXin LI 	n = (size_t)z.total_out;
370b6cee71dSXin LI 	(void)inflateEnd(&z);
371b6cee71dSXin LI 
372b6cee71dSXin LI 	/* let's keep the nul-terminate tradition */
373b6cee71dSXin LI 	(*newch)[n] = '\0';
374b6cee71dSXin LI 
375b6cee71dSXin LI 	return n;
376b6cee71dSXin LI }
377b6cee71dSXin LI #endif
378b6cee71dSXin LI 
379b6cee71dSXin LI private size_t
380b6cee71dSXin LI uncompressbuf(struct magic_set *ms, int fd, size_t method,
381b6cee71dSXin LI     const unsigned char *old, unsigned char **newch, size_t n)
382b6cee71dSXin LI {
383b6cee71dSXin LI 	int fdin[2], fdout[2];
384c2931133SXin LI 	int status;
385b6cee71dSXin LI 	ssize_t r;
386b6cee71dSXin LI 	pid_t pid;
387b6cee71dSXin LI 
388b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
389b6cee71dSXin LI         /* FIXME: This doesn't cope with bzip2 */
390b6cee71dSXin LI 	if (method == 2)
391b6cee71dSXin LI 		return uncompressgzipped(ms, old, newch, n);
392b6cee71dSXin LI #endif
393b6cee71dSXin LI 	(void)fflush(stdout);
394b6cee71dSXin LI 	(void)fflush(stderr);
395b6cee71dSXin LI 
396b6cee71dSXin LI 	if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) {
397b6cee71dSXin LI 		file_error(ms, errno, "cannot create pipe");
398b6cee71dSXin LI 		return NODATA;
399b6cee71dSXin LI 	}
400b6cee71dSXin LI 	switch (pid = fork()) {
401b6cee71dSXin LI 	case 0:	/* child */
402b6cee71dSXin LI 		(void) close(0);
403b6cee71dSXin LI 		if (fd != -1) {
404b6cee71dSXin LI 		    if (dup(fd) == -1)
405b6cee71dSXin LI 			_exit(1);
406b6cee71dSXin LI 		    (void) lseek(0, (off_t)0, SEEK_SET);
407b6cee71dSXin LI 		} else {
408b6cee71dSXin LI 		    if (dup(fdin[0]) == -1)
409b6cee71dSXin LI 			_exit(1);
410b6cee71dSXin LI 		    (void) close(fdin[0]);
411b6cee71dSXin LI 		    (void) close(fdin[1]);
412b6cee71dSXin LI 		}
413b6cee71dSXin LI 
414b6cee71dSXin LI 		(void) close(1);
415b6cee71dSXin LI 		if (dup(fdout[1]) == -1)
416b6cee71dSXin LI 			_exit(1);
417b6cee71dSXin LI 		(void) close(fdout[0]);
418b6cee71dSXin LI 		(void) close(fdout[1]);
419b6cee71dSXin LI #ifndef DEBUG
420b6cee71dSXin LI 		if (compr[method].silent)
421b6cee71dSXin LI 			(void)close(2);
422b6cee71dSXin LI #endif
423b6cee71dSXin LI 
424b6cee71dSXin LI 		(void)execvp(compr[method].argv[0],
425b6cee71dSXin LI 		    (char *const *)(intptr_t)compr[method].argv);
426b6cee71dSXin LI #ifdef DEBUG
427b6cee71dSXin LI 		(void)fprintf(stderr, "exec `%s' failed (%s)\n",
428b6cee71dSXin LI 		    compr[method].argv[0], strerror(errno));
429b6cee71dSXin LI #endif
430b6cee71dSXin LI 		exit(1);
431b6cee71dSXin LI 		/*NOTREACHED*/
432b6cee71dSXin LI 	case -1:
433b6cee71dSXin LI 		file_error(ms, errno, "could not fork");
434b6cee71dSXin LI 		return NODATA;
435b6cee71dSXin LI 
436b6cee71dSXin LI 	default: /* parent */
437b6cee71dSXin LI 		(void) close(fdout[1]);
438b6cee71dSXin LI 		if (fd == -1) {
439b6cee71dSXin LI 			(void) close(fdin[0]);
440b6cee71dSXin LI 			/*
441b6cee71dSXin LI 			 * fork again, to avoid blocking because both
442b6cee71dSXin LI 			 * pipes filled
443b6cee71dSXin LI 			 */
444b6cee71dSXin LI 			switch (fork()) {
445b6cee71dSXin LI 			case 0: /* child */
446b6cee71dSXin LI 				(void)close(fdout[0]);
447b6cee71dSXin LI 				if (swrite(fdin[1], old, n) != (ssize_t)n) {
448b6cee71dSXin LI #ifdef DEBUG
449b6cee71dSXin LI 					(void)fprintf(stderr,
450b6cee71dSXin LI 					    "Write failed (%s)\n",
451b6cee71dSXin LI 					    strerror(errno));
452b6cee71dSXin LI #endif
453b6cee71dSXin LI 					exit(1);
454b6cee71dSXin LI 				}
455b6cee71dSXin LI 				exit(0);
456b6cee71dSXin LI 				/*NOTREACHED*/
457b6cee71dSXin LI 
458b6cee71dSXin LI 			case -1:
459b6cee71dSXin LI #ifdef DEBUG
460b6cee71dSXin LI 				(void)fprintf(stderr, "Fork failed (%s)\n",
461b6cee71dSXin LI 				    strerror(errno));
462b6cee71dSXin LI #endif
463b6cee71dSXin LI 				exit(1);
464b6cee71dSXin LI 				/*NOTREACHED*/
465b6cee71dSXin LI 
466b6cee71dSXin LI 			default:  /* parent */
467c2931133SXin LI 				if (wait(&status) == -1) {
468c2931133SXin LI #ifdef DEBUG
469c2931133SXin LI 					(void)fprintf(stderr,
470c2931133SXin LI 					    "Wait failed (%s)\n",
471c2931133SXin LI 					    strerror(errno));
472c2931133SXin LI #endif
473c2931133SXin LI 					exit(1);
474c2931133SXin LI 				}
475c2931133SXin LI 				exit(WIFEXITED(status) ?
476c2931133SXin LI 				    WEXITSTATUS(status) : 1);
477c2931133SXin LI 				/*NOTREACHED*/
478b6cee71dSXin LI 			}
479b6cee71dSXin LI 			(void) close(fdin[1]);
480b6cee71dSXin LI 			fdin[1] = -1;
481b6cee71dSXin LI 		}
482b6cee71dSXin LI 
483b6cee71dSXin LI 		if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) {
484b6cee71dSXin LI #ifdef DEBUG
485b6cee71dSXin LI 			(void)fprintf(stderr, "Malloc failed (%s)\n",
486b6cee71dSXin LI 			    strerror(errno));
487b6cee71dSXin LI #endif
488c2931133SXin LI 			n = NODATA;
489b6cee71dSXin LI 			goto err;
490b6cee71dSXin LI 		}
491b6cee71dSXin LI 		if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) {
492b6cee71dSXin LI #ifdef DEBUG
493b6cee71dSXin LI 			(void)fprintf(stderr, "Read failed (%s)\n",
494b6cee71dSXin LI 			    strerror(errno));
495b6cee71dSXin LI #endif
496b6cee71dSXin LI 			free(*newch);
497c2931133SXin LI 			n = NODATA;
498b6cee71dSXin LI 			*newch = NULL;
499b6cee71dSXin LI 			goto err;
500b6cee71dSXin LI 		} else {
501b6cee71dSXin LI 			n = r;
502b6cee71dSXin LI 		}
503b6cee71dSXin LI  		/* NUL terminate, as every buffer is handled here. */
504b6cee71dSXin LI  		(*newch)[n] = '\0';
505b6cee71dSXin LI err:
506b6cee71dSXin LI 		if (fdin[1] != -1)
507b6cee71dSXin LI 			(void) close(fdin[1]);
508b6cee71dSXin LI 		(void) close(fdout[0]);
509c2931133SXin LI 		if (wait(&status) == -1) {
510c2931133SXin LI #ifdef DEBUG
511c2931133SXin LI 			(void)fprintf(stderr, "Wait failed (%s)\n",
512c2931133SXin LI 			    strerror(errno));
513b6cee71dSXin LI #endif
514c2931133SXin LI 			n = NODATA;
515*4460e5b0SXin LI 		} else if (!WIFEXITED(status)) {
516c2931133SXin LI #ifdef DEBUG
517*4460e5b0SXin LI 			(void)fprintf(stderr, "Child not exited (0x%x)\n",
518*4460e5b0SXin LI 			    status);
519c2931133SXin LI #endif
520*4460e5b0SXin LI 		} else if (WEXITSTATUS(status) != 0) {
521*4460e5b0SXin LI #ifdef DEBUG
522*4460e5b0SXin LI 			(void)fprintf(stderr, "Child exited (0x%d)\n",
523*4460e5b0SXin LI 			    WEXITSTATUS(status));
524*4460e5b0SXin LI #endif
525c2931133SXin LI 		}
526c2931133SXin LI 
527b6cee71dSXin LI 		(void) close(fdin[0]);
528b6cee71dSXin LI 
529b6cee71dSXin LI 		return n;
530b6cee71dSXin LI 	}
531b6cee71dSXin LI }
532b6cee71dSXin LI #endif
533