compress.c revision 103373
1/*
2 * compress routines:
3 *	zmagic() - returns 0 if not recognized, uncompresses and prints
4 *		   information if recognized
5 *	uncompress(method, old, n, newch) - uncompress old into new,
6 *					    using method, return sizeof new
7 */
8#include "file.h"
9#include <stdlib.h>
10#ifdef HAVE_UNISTD_H
11#include <unistd.h>
12#endif
13#include <string.h>
14#ifdef HAVE_SYS_WAIT_H
15#include <sys/wait.h>
16#endif
17#ifdef HAVE_LIBZ
18#include <zlib.h>
19#endif
20
21#ifndef lint
22FILE_RCSID("@(#)$Id: compress.c,v 1.25 2002/07/03 18:26:37 christos Exp $")
23#endif
24
25
26static struct {
27	const char *magic;
28	int   maglen;
29	const char *const argv[3];
30	int	 silent;
31} compr[] = {
32	{ "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },		/* compressed */
33	/* Uncompress can get stuck; so use gzip first if we have it
34	 * Idea from Damien Clark, thanks! */
35	{ "\037\235", 2, { "uncompress", "-c", NULL }, 1 },	/* compressed */
36	{ "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },		/* gzipped */
37	{ "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },		/* frozen */
38	{ "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },		/* SCO LZH */
39	/* the standard pack utilities do not accept standard input */
40	{ "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },		/* packed */
41	{ "BZh",      3, { "bzip2", "-cd", NULL }, 1 },		/* bzip2-ed */
42};
43
44static int ncompr = sizeof(compr) / sizeof(compr[0]);
45
46
47static int swrite(int, const void *, size_t);
48static int sread(int, void *, size_t);
49static int uncompressbuf(int, const unsigned char *, unsigned char **, int);
50#ifdef HAVE_LIBZ
51static int uncompressgzipped(const unsigned char *, unsigned char **, int);
52#endif
53
54int
55zmagic(const char *fname, unsigned char *buf, int nbytes)
56{
57	unsigned char *newbuf;
58	int newsize;
59	int i;
60
61	for (i = 0; i < ncompr; i++) {
62		if (nbytes < compr[i].maglen)
63			continue;
64		if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
65		    (newsize = uncompressbuf(i, buf, &newbuf, nbytes)) != 0) {
66			tryit(fname, newbuf, newsize, 1);
67			free(newbuf);
68			printf(" (");
69			tryit(fname, buf, nbytes, 0);
70			printf(")");
71			return 1;
72		}
73	}
74
75	if (i == ncompr)
76		return 0;
77
78	return 1;
79}
80
81/*
82 * `safe' write for sockets and pipes.
83 */
84static int
85swrite(int fd, const void *buf, size_t n)
86{
87	int rv;
88	size_t rn = n;
89
90	do
91		switch (rv = write(fd, buf, n)) {
92		case -1:
93			if (errno == EINTR)
94				continue;
95			return -1;
96		default:
97			n -= rv;
98			buf = ((const char *)buf) + rv;
99			break;
100		}
101	while (n > 0);
102	return rn;
103}
104
105
106/*
107 * `safe' read for sockets and pipes.
108 */
109static int
110sread(int fd, void *buf, size_t n)
111{
112	int rv;
113	size_t rn = n;
114
115	do
116		switch (rv = read(fd, buf, n)) {
117		case -1:
118			if (errno == EINTR)
119				continue;
120			return -1;
121		case 0:
122			return rn - n;
123		default:
124			n -= rv;
125			buf = ((char *)buf) + rv;
126			break;
127		}
128	while (n > 0);
129	return rn;
130}
131
132int
133pipe2file(int fd, void *startbuf, size_t nbytes)
134{
135	char buf[4096];
136	int r, tfd;
137
138	(void)strcpy(buf, "/tmp/file.XXXXXX");
139#ifndef HAVE_MKSTEMP
140	{
141		char *ptr = mktemp(buf);
142		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
143		r = errno;
144		(void)unlink(ptr);
145		errno = r;
146	}
147#else
148	tfd = mkstemp(buf);
149	r = errno;
150	(void)unlink(buf);
151	errno = r;
152#endif
153	if (tfd == -1) {
154		error("Can't create temporary file for pipe copy (%s)\n",
155		    strerror(errno));
156		/*NOTREACHED*/
157	}
158
159	if (swrite(tfd, startbuf, nbytes) != nbytes)
160		r = 1;
161	else {
162		while ((r = sread(fd, buf, sizeof(buf))) > 0)
163			if (swrite(tfd, buf, r) != r)
164				break;
165	}
166
167	switch (r) {
168	case -1:
169		error("Error copying from pipe to temp file (%s)\n",
170		    strerror(errno));
171		/*NOTREACHED*/
172	case 0:
173		break;
174	default:
175		error("Error while writing to temp file (%s)\n",
176		    strerror(errno));
177		/*NOTREACHED*/
178	}
179
180	/*
181	 * We duplicate the file descriptor, because fclose on a
182	 * tmpfile will delete the file, but any open descriptors
183	 * can still access the phantom inode.
184	 */
185	if ((fd = dup2(tfd, fd)) == -1) {
186		error("Couldn't dup destcriptor for temp file(%s)\n",
187		    strerror(errno));
188		/*NOTREACHED*/
189	}
190	(void)close(tfd);
191	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
192		error("Couldn't seek on temp file (%s)\n", strerror(errno));
193		/*NOTREACHED*/
194	}
195	return fd;
196}
197
198#ifdef HAVE_LIBZ
199
200#define FHCRC		(1 << 1)
201#define FEXTRA		(1 << 2)
202#define FNAME		(1 << 3)
203#define FCOMMENT	(1 << 4)
204
205static int
206uncompressgzipped(const unsigned char *old, unsigned char **newch, int n)
207{
208	unsigned char flg = old[3];
209	int data_start = 10;
210	z_stream z;
211	int rc;
212
213	if (flg & FEXTRA)
214		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
215	if (flg & FNAME) {
216		while(old[data_start])
217			data_start++;
218		data_start++;
219	}
220	if(flg & FCOMMENT) {
221		while(old[data_start])
222			data_start++;
223		data_start++;
224	}
225	if(flg & FHCRC)
226		data_start += 2;
227
228	if ((*newch = (unsigned char *)malloc(HOWMANY + 1)) == NULL) {
229		return 0;
230	}
231
232	z.next_in = (Bytef *)(old + data_start);
233	z.avail_in = n - data_start;
234	z.next_out = *newch;
235	z.avail_out = HOWMANY;
236	z.zalloc = Z_NULL;
237	z.zfree = Z_NULL;
238	z.opaque = Z_NULL;
239
240	rc = inflateInit2(&z, -15);
241	if (rc != Z_OK) {
242		(void) fprintf(stderr,"%s: zlib: %s\n", progname, z.msg);
243		return 0;
244	}
245
246	rc = inflate(&z, Z_SYNC_FLUSH);
247	if (rc != Z_OK && rc != Z_STREAM_END) {
248		fprintf(stderr,"%s: zlib: %s\n", progname, z.msg);
249		return 0;
250	}
251
252	n = z.total_out;
253	inflateEnd(&z);
254
255	/* let's keep the nul-terminate tradition */
256	(*newch)[n++] = '\0';
257
258	return n;
259}
260#endif
261
262static int
263uncompressbuf(int method, const unsigned char *old, unsigned char **newch,
264	      int n)
265{
266	int fdin[2], fdout[2];
267
268	/* The buffer is NUL terminated, and we don't need that. */
269	n--;
270
271#ifdef HAVE_LIBZ
272	if (method == 2)
273		return uncompressgzipped(old,newch,n);
274#endif
275
276	if (pipe(fdin) == -1 || pipe(fdout) == -1) {
277		error("cannot create pipe (%s).\n", strerror(errno));
278		/*NOTREACHED*/
279	}
280	switch (fork()) {
281	case 0:	/* child */
282		(void) close(0);
283		(void) dup(fdin[0]);
284		(void) close(fdin[0]);
285		(void) close(fdin[1]);
286
287		(void) close(1);
288		(void) dup(fdout[1]);
289		(void) close(fdout[0]);
290		(void) close(fdout[1]);
291		if (compr[method].silent)
292			(void) close(2);
293
294		execvp(compr[method].argv[0],
295		       (char *const *)compr[method].argv);
296		exit(1);
297		/*NOTREACHED*/
298	case -1:
299		error("could not fork (%s).\n", strerror(errno));
300		/*NOTREACHED*/
301
302	default: /* parent */
303		(void) close(fdin[0]);
304		(void) close(fdout[1]);
305		if (swrite(fdin[1], old, n) != n) {
306			n = 0;
307			goto err;
308		}
309		(void) close(fdin[1]);
310		fdin[1] = -1;
311		if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) {
312			n = 0;
313			goto err;
314		}
315		if ((n = sread(fdout[0], *newch, HOWMANY)) <= 0) {
316			free(*newch);
317			n = 0;
318			goto err;
319		}
320 		/* NUL terminate, as every buffer is handled here. */
321 		(*newch)[n++] = '\0';
322err:
323		if (fdin[1] != -1)
324			(void) close(fdin[1]);
325		(void) close(fdout[0]);
326		(void) wait(NULL);
327		return n;
328	}
329}
330