compress.c revision 302555
1/*
2 * Copyright (c) Ian F. Darwin 1986-1995.
3 * Software written by Ian F. Darwin and others;
4 * maintained 1995-present by Christos Zoulas and others.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice immediately at the beginning of the file, without modification,
11 *    this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28/*
29 * compress routines:
30 *	zmagic() - returns 0 if not recognized, uncompresses and prints
31 *		   information if recognized
32 *	uncompress(method, old, n, newch) - uncompress old into new,
33 *					    using method, return sizeof new
34 */
35#include "file.h"
36
37#ifndef lint
38FILE_RCSID("@(#)$File: compress.c,v 1.97 2016/05/13 23:02:28 christos Exp $")
39#endif
40
41#include "magic.h"
42#include <stdlib.h>
43#ifdef HAVE_UNISTD_H
44#include <unistd.h>
45#endif
46#include <string.h>
47#include <errno.h>
48#include <ctype.h>
49#include <stdarg.h>
50#ifdef HAVE_SIGNAL_H
51#include <signal.h>
52# ifndef HAVE_SIG_T
53typedef void (*sig_t)(int);
54# endif /* HAVE_SIG_T */
55#endif
56#if !defined(__MINGW32__) && !defined(WIN32)
57#include <sys/ioctl.h>
58#endif
59#ifdef HAVE_SYS_WAIT_H
60#include <sys/wait.h>
61#endif
62#if defined(HAVE_SYS_TIME_H)
63#include <sys/time.h>
64#endif
65#if defined(HAVE_ZLIB_H)
66#define BUILTIN_DECOMPRESS
67#include <zlib.h>
68#endif
69#ifdef DEBUG
70int tty = -1;
71#define DPRINTF(...)	do { \
72	if (tty == -1) \
73		tty = open("/dev/tty", O_RDWR); \
74	if (tty == -1) \
75		abort(); \
76	dprintf(tty, __VA_ARGS__); \
77} while (/*CONSTCOND*/0)
78#else
79#define DPRINTF(...)
80#endif
81
82#ifdef ZLIBSUPPORT
83/*
84 * The following python code is not really used because ZLIBSUPPORT is only
85 * defined if we have a built-in zlib, and the built-in zlib handles that.
86 */
87static const char zlibcode[] =
88    "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))";
89
90static const char *zlib_args[] = { "python", "-c", zlibcode, NULL };
91
92static int
93zlibcmp(const unsigned char *buf)
94{
95	unsigned short x = 1;
96	unsigned char *s = (unsigned char *)&x;
97
98	if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0)
99		return 0;
100	if (s[0] != 1)	/* endianness test */
101		x = buf[0] | (buf[1] << 8);
102	else
103		x = buf[1] | (buf[0] << 8);
104	if (x % 31)
105		return 0;
106	return 1;
107}
108#endif
109
110#define gzip_flags "-cd"
111#define lrzip_flags "-do"
112#define lzip_flags gzip_flags
113
114static const char *gzip_args[] = {
115	"gzip", gzip_flags, NULL
116};
117static const char *uncompress_args[] = {
118	"uncompress", "-c", NULL
119};
120static const char *bzip2_args[] = {
121	"bzip2", "-cd", NULL
122};
123static const char *lzip_args[] = {
124	"lzip", lzip_flags, NULL
125};
126static const char *xz_args[] = {
127	"xz", "-cd", NULL
128};
129static const char *lrzip_args[] = {
130	"lrzip", lrzip_flags, NULL
131};
132static const char *lz4_args[] = {
133	"lz4", "-cd", NULL
134};
135
136private const struct {
137	const void *magic;
138	size_t maglen;
139	const char **argv;
140} compr[] = {
141	{ "\037\235",	2, gzip_args },		/* compressed */
142	/* Uncompress can get stuck; so use gzip first if we have it
143	 * Idea from Damien Clark, thanks! */
144	{ "\037\235",	2, uncompress_args },	/* compressed */
145	{ "\037\213",	2, gzip_args },		/* gzipped */
146	{ "\037\236",	2, gzip_args },		/* frozen */
147	{ "\037\240",	2, gzip_args },		/* SCO LZH */
148	/* the standard pack utilities do not accept standard input */
149	{ "\037\036",	2, gzip_args },		/* packed */
150	{ "PK\3\4",	4, gzip_args },		/* pkzipped, */
151	/* ...only first file examined */
152	{ "BZh",	3, bzip2_args },	/* bzip2-ed */
153	{ "LZIP",	4, lzip_args },		/* lzip-ed */
154 	{ "\3757zXZ\0",	6, xz_args },		/* XZ Utils */
155 	{ "LRZI",	4, lrzip_args },	/* LRZIP */
156 	{ "\004\"M\030",4, lz4_args },		/* LZ4 */
157#ifdef ZLIBSUPPORT
158	{ zlibcmp,	0, zlib_args },		/* zlib */
159#endif
160};
161
162#define OKDATA 	0
163#define NODATA	1
164#define ERRDATA	2
165
166private ssize_t swrite(int, const void *, size_t);
167#if HAVE_FORK
168private size_t ncompr = sizeof(compr) / sizeof(compr[0]);
169private int uncompressbuf(int, size_t, size_t, const unsigned char *,
170    unsigned char **, size_t *);
171#ifdef BUILTIN_DECOMPRESS
172private int uncompresszlib(const unsigned char *, unsigned char **, size_t,
173    size_t *, int);
174private int uncompressgzipped(const unsigned char *, unsigned char **, size_t,
175    size_t *);
176#endif
177static int makeerror(unsigned char **, size_t *, const char *, ...)
178    __attribute__((__format__(__printf__, 3, 4)));
179private const char *methodname(size_t);
180
181protected int
182file_zmagic(struct magic_set *ms, int fd, const char *name,
183    const unsigned char *buf, size_t nbytes)
184{
185	unsigned char *newbuf = NULL;
186	size_t i, nsz;
187	char *rbuf;
188	file_pushbuf_t *pb;
189	int urv, prv, rv = 0;
190	int mime = ms->flags & MAGIC_MIME;
191#ifdef HAVE_SIGNAL_H
192	sig_t osigpipe;
193#endif
194
195	if ((ms->flags & MAGIC_COMPRESS) == 0)
196		return 0;
197
198#ifdef HAVE_SIGNAL_H
199	osigpipe = signal(SIGPIPE, SIG_IGN);
200#endif
201	for (i = 0; i < ncompr; i++) {
202		int zm;
203		if (nbytes < compr[i].maglen)
204			continue;
205#ifdef ZLIBSUPPORT
206		if (compr[i].maglen == 0)
207			zm = (CAST(int (*)(const unsigned char *),
208			    CCAST(void *, compr[i].magic)))(buf);
209		else
210#endif
211			zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0;
212
213		if (!zm)
214			continue;
215		nsz = nbytes;
216		urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz);
217		DPRINTF("uncompressbuf = %d, %s, %zu\n", urv, (char *)newbuf,
218		    nsz);
219		switch (urv) {
220		case OKDATA:
221		case ERRDATA:
222
223			ms->flags &= ~MAGIC_COMPRESS;
224			if (urv == ERRDATA)
225				prv = file_printf(ms, "%s ERROR: %s",
226				    methodname(i), newbuf);
227			else
228				prv = file_buffer(ms, -1, name, newbuf, nsz);
229			if (prv == -1)
230				goto error;
231			rv = 1;
232			if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
233				goto out;
234			if (mime != MAGIC_MIME && mime != 0)
235				goto out;
236			if ((file_printf(ms,
237			    mime ? " compressed-encoding=" : " (")) == -1)
238				goto error;
239			if ((pb = file_push_buffer(ms)) == NULL)
240				goto error;
241			/*
242			 * XXX: If file_buffer fails here, we overwrite
243			 * the compressed text. FIXME.
244			 */
245			if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
246				goto error;
247			if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
248				if (file_printf(ms, "%s", rbuf) == -1) {
249					free(rbuf);
250					goto error;
251				}
252				free(rbuf);
253			}
254			if (!mime && file_printf(ms, ")") == -1)
255				goto error;
256			/*FALLTHROUGH*/
257		case NODATA:
258			break;
259		default:
260			abort();
261			/*NOTREACHED*/
262		error:
263			rv = -1;
264			break;
265		}
266	}
267out:
268	DPRINTF("rv = %d\n", rv);
269
270#ifdef HAVE_SIGNAL_H
271	(void)signal(SIGPIPE, osigpipe);
272#endif
273	free(newbuf);
274	ms->flags |= MAGIC_COMPRESS;
275	DPRINTF("Zmagic returns %d\n", rv);
276	return rv;
277}
278#endif
279/*
280 * `safe' write for sockets and pipes.
281 */
282private ssize_t
283swrite(int fd, const void *buf, size_t n)
284{
285	ssize_t rv;
286	size_t rn = n;
287
288	do
289		switch (rv = write(fd, buf, n)) {
290		case -1:
291			if (errno == EINTR)
292				continue;
293			return -1;
294		default:
295			n -= rv;
296			buf = CAST(const char *, buf) + rv;
297			break;
298		}
299	while (n > 0);
300	return rn;
301}
302
303
304/*
305 * `safe' read for sockets and pipes.
306 */
307protected ssize_t
308sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
309{
310	ssize_t rv;
311#ifdef FIONREAD
312	int t = 0;
313#endif
314	size_t rn = n;
315
316	if (fd == STDIN_FILENO)
317		goto nocheck;
318
319#ifdef FIONREAD
320	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
321#ifdef FD_ZERO
322		ssize_t cnt;
323		for (cnt = 0;; cnt++) {
324			fd_set check;
325			struct timeval tout = {0, 100 * 1000};
326			int selrv;
327
328			FD_ZERO(&check);
329			FD_SET(fd, &check);
330
331			/*
332			 * Avoid soft deadlock: do not read if there
333			 * is nothing to read from sockets and pipes.
334			 */
335			selrv = select(fd + 1, &check, NULL, NULL, &tout);
336			if (selrv == -1) {
337				if (errno == EINTR || errno == EAGAIN)
338					continue;
339			} else if (selrv == 0 && cnt >= 5) {
340				return 0;
341			} else
342				break;
343		}
344#endif
345		(void)ioctl(fd, FIONREAD, &t);
346	}
347
348	if (t > 0 && (size_t)t < n) {
349		n = t;
350		rn = n;
351	}
352#endif
353
354nocheck:
355	do
356		switch ((rv = read(fd, buf, n))) {
357		case -1:
358			if (errno == EINTR)
359				continue;
360			return -1;
361		case 0:
362			return rn - n;
363		default:
364			n -= rv;
365			buf = ((char *)buf) + rv;
366			break;
367		}
368	while (n > 0);
369	return rn;
370}
371
372protected int
373file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
374    size_t nbytes)
375{
376	char buf[4096];
377	ssize_t r;
378	int tfd;
379
380	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
381#ifndef HAVE_MKSTEMP
382	{
383		char *ptr = mktemp(buf);
384		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
385		r = errno;
386		(void)unlink(ptr);
387		errno = r;
388	}
389#else
390	{
391		int te;
392		tfd = mkstemp(buf);
393		te = errno;
394		(void)unlink(buf);
395		errno = te;
396	}
397#endif
398	if (tfd == -1) {
399		file_error(ms, errno,
400		    "cannot create temporary file for pipe copy");
401		return -1;
402	}
403
404	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
405		r = 1;
406	else {
407		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
408			if (swrite(tfd, buf, (size_t)r) != r)
409				break;
410	}
411
412	switch (r) {
413	case -1:
414		file_error(ms, errno, "error copying from pipe to temp file");
415		return -1;
416	case 0:
417		break;
418	default:
419		file_error(ms, errno, "error while writing to temp file");
420		return -1;
421	}
422
423	/*
424	 * We duplicate the file descriptor, because fclose on a
425	 * tmpfile will delete the file, but any open descriptors
426	 * can still access the phantom inode.
427	 */
428	if ((fd = dup2(tfd, fd)) == -1) {
429		file_error(ms, errno, "could not dup descriptor for temp file");
430		return -1;
431	}
432	(void)close(tfd);
433	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
434		file_badseek(ms);
435		return -1;
436	}
437	return fd;
438}
439#if HAVE_FORK
440#ifdef BUILTIN_DECOMPRESS
441
442#define FHCRC		(1 << 1)
443#define FEXTRA		(1 << 2)
444#define FNAME		(1 << 3)
445#define FCOMMENT	(1 << 4)
446
447
448private int
449uncompressgzipped(const unsigned char *old, unsigned char **newch,
450    size_t bytes_max, size_t *n)
451{
452	unsigned char flg = old[3];
453	size_t data_start = 10;
454
455	if (flg & FEXTRA) {
456		if (data_start + 1 >= *n)
457			goto err;
458		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
459	}
460	if (flg & FNAME) {
461		while(data_start < *n && old[data_start])
462			data_start++;
463		data_start++;
464	}
465	if (flg & FCOMMENT) {
466		while(data_start < *n && old[data_start])
467			data_start++;
468		data_start++;
469	}
470	if (flg & FHCRC)
471		data_start += 2;
472
473	if (data_start >= *n)
474		goto err;
475
476	*n -= data_start;
477	old += data_start;
478	return uncompresszlib(old, newch, bytes_max, n, 0);
479err:
480	return makeerror(newch, n, "File too short");
481}
482
483private int
484uncompresszlib(const unsigned char *old, unsigned char **newch,
485    size_t bytes_max, size_t *n, int zlib)
486{
487	int rc;
488	z_stream z;
489
490	if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL)
491		return makeerror(newch, n, "No buffer, %s", strerror(errno));
492
493	z.next_in = CCAST(Bytef *, old);
494	z.avail_in = CAST(uint32_t, *n);
495	z.next_out = *newch;
496	z.avail_out = bytes_max;
497	z.zalloc = Z_NULL;
498	z.zfree = Z_NULL;
499	z.opaque = Z_NULL;
500
501	/* LINTED bug in header macro */
502	rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
503	if (rc != Z_OK)
504		goto err;
505
506	rc = inflate(&z, Z_SYNC_FLUSH);
507	if (rc != Z_OK && rc != Z_STREAM_END)
508		goto err;
509
510	*n = (size_t)z.total_out;
511	rc = inflateEnd(&z);
512	if (rc != Z_OK)
513		goto err;
514
515	/* let's keep the nul-terminate tradition */
516	(*newch)[*n] = '\0';
517
518	return OKDATA;
519err:
520	strlcpy((char *)*newch, z.msg, bytes_max);
521	*n = strlen((char *)*newch);
522	return ERRDATA;
523}
524#endif
525
526static int
527makeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
528{
529	char *msg;
530	va_list ap;
531	int rv;
532
533	va_start(ap, fmt);
534	rv = vasprintf(&msg, fmt, ap);
535	va_end(ap);
536	if (rv < 0) {
537		*buf = NULL;
538		*len = 0;
539		return NODATA;
540	}
541	*buf = (unsigned char *)msg;
542	*len = strlen(msg);
543	return ERRDATA;
544}
545
546static void
547closefd(int *fd, size_t i)
548{
549	if (fd[i] == -1)
550		return;
551	(void) close(fd[i]);
552	fd[i] = -1;
553}
554
555static void
556closep(int *fd)
557{
558	size_t i;
559	for (i = 0; i < 2; i++)
560		closefd(fd, i);
561}
562
563static void
564copydesc(int i, int *fd)
565{
566	int j = fd[i == STDIN_FILENO ? 0 : 1];
567	if (j == i)
568		return;
569	if (dup2(j, i) == -1) {
570		DPRINTF("dup(%d, %d) failed (%s)\n", j, i, strerror(errno));
571		exit(1);
572	}
573	closep(fd);
574}
575
576static void
577writechild(int fdp[3][2], const void *old, size_t n)
578{
579	int status;
580
581	closefd(fdp[STDIN_FILENO], 0);
582	/*
583	 * fork again, to avoid blocking because both
584	 * pipes filled
585	 */
586	switch (fork()) {
587	case 0: /* child */
588		closefd(fdp[STDOUT_FILENO], 0);
589		if (swrite(fdp[STDIN_FILENO][1], old, n) != (ssize_t)n) {
590			DPRINTF("Write failed (%s)\n", strerror(errno));
591			exit(1);
592		}
593		exit(0);
594		/*NOTREACHED*/
595
596	case -1:
597		DPRINTF("Fork failed (%s)\n", strerror(errno));
598		exit(1);
599		/*NOTREACHED*/
600
601	default:  /* parent */
602		if (wait(&status) == -1) {
603			DPRINTF("Wait failed (%s)\n", strerror(errno));
604			exit(1);
605		}
606		DPRINTF("Grandchild wait return %#x\n", status);
607	}
608	closefd(fdp[STDIN_FILENO], 1);
609}
610
611static ssize_t
612filter_error(unsigned char *ubuf, ssize_t n)
613{
614	char *p;
615	char *buf;
616
617	ubuf[n] = '\0';
618	buf = (char *)ubuf;
619	while (isspace((unsigned char)*buf))
620		buf++;
621	DPRINTF("Filter error[[[%s]]]\n", buf);
622	if ((p = strchr((char *)buf, '\n')) != NULL)
623		*p = '\0';
624	if ((p = strchr((char *)buf, ';')) != NULL)
625		*p = '\0';
626	if ((p = strrchr((char *)buf, ':')) != NULL) {
627		++p;
628		while (isspace((unsigned char)*p))
629			p++;
630		n = strlen(p);
631		memmove(ubuf, p, n + 1);
632	}
633	DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
634	if (islower(*ubuf))
635		*ubuf = toupper(*ubuf);
636	return n;
637}
638
639private const char *
640methodname(size_t method)
641{
642#ifdef BUILTIN_DECOMPRESS
643        /* FIXME: This doesn't cope with bzip2 */
644	if (method == 2 || compr[method].maglen == 0)
645	    return "zlib";
646#endif
647	return compr[method].argv[0];
648}
649
650private int
651uncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
652    unsigned char **newch, size_t* n)
653{
654	int fdp[3][2];
655	int status, rv;
656	size_t i;
657	ssize_t r;
658
659#ifdef BUILTIN_DECOMPRESS
660        /* FIXME: This doesn't cope with bzip2 */
661	if (method == 2)
662		return uncompressgzipped(old, newch, bytes_max, n);
663	if (compr[method].maglen == 0)
664		return uncompresszlib(old, newch, bytes_max, n, 1);
665#endif
666	(void)fflush(stdout);
667	(void)fflush(stderr);
668
669	for (i = 0; i < __arraycount(fdp); i++)
670		fdp[i][0] = fdp[i][1] = -1;
671
672	if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) ||
673	    pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) {
674		closep(fdp[STDIN_FILENO]);
675		closep(fdp[STDOUT_FILENO]);
676		return makeerror(newch, n, "Cannot create pipe, %s",
677		    strerror(errno));
678	}
679	switch (fork()) {
680	case 0:	/* child */
681		if (fd != -1) {
682			fdp[STDIN_FILENO][0] = fd;
683			(void) lseek(fd, (off_t)0, SEEK_SET);
684		}
685
686		for (i = 0; i < __arraycount(fdp); i++)
687			copydesc(i, fdp[i]);
688
689		(void)execvp(compr[method].argv[0],
690		    (char *const *)(intptr_t)compr[method].argv);
691		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
692		    compr[method].argv[0], strerror(errno));
693		exit(1);
694		/*NOTREACHED*/
695	case -1:
696		return makeerror(newch, n, "Cannot fork, %s",
697		    strerror(errno));
698
699	default: /* parent */
700		for (i = 1; i < __arraycount(fdp); i++)
701			closefd(fdp[i], 1);
702
703		/* Write the buffer data to the child, if we don't have fd */
704		if (fd == -1)
705			writechild(fdp, old, *n);
706
707		*newch = CAST(unsigned char *, malloc(bytes_max + 1));
708		if (*newch == NULL) {
709			rv = makeerror(newch, n, "No buffer, %s",
710			    strerror(errno));
711			goto err;
712		}
713		rv = OKDATA;
714		if ((r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0)) > 0)
715			break;
716		DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
717		    r != -1 ? strerror(errno) : "no data");
718
719		rv = ERRDATA;
720		if (r == 0 &&
721		    (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0)
722		{
723			r = filter_error(*newch, r);
724			break;
725		}
726		free(*newch);
727		if  (r == 0)
728			rv = makeerror(newch, n, "Read failed, %s",
729			    strerror(errno));
730		else
731			rv = makeerror(newch, n, "No data");
732		goto err;
733	}
734
735	*n = r;
736	/* NUL terminate, as every buffer is handled here. */
737	(*newch)[*n] = '\0';
738err:
739	closefd(fdp[STDIN_FILENO], 1);
740	closefd(fdp[STDOUT_FILENO], 0);
741	closefd(fdp[STDERR_FILENO], 0);
742	if (wait(&status) == -1) {
743		free(*newch);
744		rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
745		DPRINTF("Child wait return %#x\n", status);
746	} else if (!WIFEXITED(status)) {
747		DPRINTF("Child not exited (0x%x)\n", status);
748	} else if (WEXITSTATUS(status) != 0) {
749		DPRINTF("Child exited (0x%d)\n", WEXITSTATUS(status));
750	}
751
752	closefd(fdp[STDIN_FILENO], 0);
753	DPRINTF("Returning %p n=%zu rv=%d\n", *newch, *n, rv);
754
755	return rv;
756}
757#endif
758