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