Merge from vendor branch NTPD:
[dragonfly.git] / contrib / file / compress.c
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
22 FILE_RCSID("@(#)$Id: compress.c,v 1.25 2002/07/03 18:26:37 christos Exp $")
23 #endif
24
25
26 static 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
44 static int ncompr = sizeof(compr) / sizeof(compr[0]);
45
46
47 static int swrite(int, const void *, size_t);
48 static int sread(int, void *, size_t);
49 static int uncompressbuf(int, const unsigned char *, unsigned char **, int);
50 #ifdef HAVE_LIBZ
51 static int uncompressgzipped(const unsigned char *, unsigned char **, int);
52 #endif
53
54 int
55 zmagic(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  */
84 static int
85 swrite(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  */
109 static int
110 sread(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
132 int
133 pipe2file(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
205 static int
206 uncompressgzipped(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
262 static int
263 uncompressbuf(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';
322 err:
323                 if (fdin[1] != -1)
324                         (void) close(fdin[1]);
325                 (void) close(fdout[0]);
326                 (void) wait(NULL);
327                 return n;
328         }
329 }