gzip.c revision 180727
1/* $NetBSD: gzip.c,v 1.92 2008/07/21 14:19:22 lukem Exp $ */ 2 3/*- 4 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, 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 ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * 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 30#include <sys/cdefs.h> 31#ifndef lint 32__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\ 33 Matthew R. Green. All rights reserved."); 34__RCSID("$FreeBSD: head/usr.bin/gzip/gzip.c 180727 2008-07-23 00:03:36Z delphij $"); 35#endif /* not lint */ 36 37/* 38 * gzip.c -- GPL free gzip using zlib. 39 * 40 * RFC 1950 covers the zlib format 41 * RFC 1951 covers the deflate format 42 * RFC 1952 covers the gzip format 43 * 44 * TODO: 45 * - use mmap where possible 46 * - handle some signals better (remove outfile?) 47 * - make bzip2/compress -v/-t/-l support work as well as possible 48 */ 49 50#include <sys/param.h> 51#include <sys/stat.h> 52#include <sys/time.h> 53 54#include <inttypes.h> 55#include <unistd.h> 56#include <stdio.h> 57#include <string.h> 58#include <stdlib.h> 59#include <err.h> 60#include <errno.h> 61#include <fcntl.h> 62#include <zlib.h> 63#include <fts.h> 64#include <libgen.h> 65#include <stdarg.h> 66#include <getopt.h> 67#include <time.h> 68 69#ifndef PRIdOFF 70#define PRIdOFF PRId64 71#endif 72 73/* what type of file are we dealing with */ 74enum filetype { 75 FT_GZIP, 76#ifndef NO_BZIP2_SUPPORT 77 FT_BZIP2, 78#endif 79#ifndef NO_COMPRESS_SUPPORT 80 FT_Z, 81#endif 82 FT_LAST, 83 FT_UNKNOWN 84}; 85 86#ifndef NO_BZIP2_SUPPORT 87#include <bzlib.h> 88 89#define BZ2_SUFFIX ".bz2" 90#define BZIP2_MAGIC "\102\132\150" 91#endif 92 93#ifndef NO_COMPRESS_SUPPORT 94#define Z_SUFFIX ".Z" 95#define Z_MAGIC "\037\235" 96#endif 97 98#define GZ_SUFFIX ".gz" 99 100#define BUFLEN (64 * 1024) 101 102#define GZIP_MAGIC0 0x1F 103#define GZIP_MAGIC1 0x8B 104#define GZIP_OMAGIC1 0x9E 105 106#define GZIP_TIMESTAMP (off_t)4 107#define GZIP_ORIGNAME (off_t)10 108 109#define HEAD_CRC 0x02 110#define EXTRA_FIELD 0x04 111#define ORIG_NAME 0x08 112#define COMMENT 0x10 113 114#define OS_CODE 3 /* Unix */ 115 116typedef struct { 117 const char *zipped; 118 int ziplen; 119 const char *normal; /* for unzip - must not be longer than zipped */ 120} suffixes_t; 121static suffixes_t suffixes[] = { 122#define SUFFIX(Z, N) {Z, sizeof Z - 1, N} 123 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ 124#ifndef SMALL 125 SUFFIX(GZ_SUFFIX, ""), 126 SUFFIX(".z", ""), 127 SUFFIX("-gz", ""), 128 SUFFIX("-z", ""), 129 SUFFIX("_z", ""), 130 SUFFIX(".taz", ".tar"), 131 SUFFIX(".tgz", ".tar"), 132#ifndef NO_BZIP2_SUPPORT 133 SUFFIX(BZ2_SUFFIX, ""), 134 SUFFIX(".tbz", ".tar"), 135 SUFFIX(".tbz2", ".tar"), 136#endif 137#ifndef NO_COMPRESS_SUPPORT 138 SUFFIX(Z_SUFFIX, ""), 139#endif 140 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ 141#endif /* SMALL */ 142#undef SUFFIX 143}; 144#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) 145 146static const char gzip_version[] = "FreeBSD gzip 20070711"; 147 148#ifndef SMALL 149static const char gzip_copyright[] = \ 150" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" 151" All rights reserved.\n" 152"\n" 153" Redistribution and use in source and binary forms, with or without\n" 154" modification, are permitted provided that the following conditions\n" 155" are met:\n" 156" 1. Redistributions of source code must retain the above copyright\n" 157" notice, this list of conditions and the following disclaimer.\n" 158" 2. Redistributions in binary form must reproduce the above copyright\n" 159" notice, this list of conditions and the following disclaimer in the\n" 160" documentation and/or other materials provided with the distribution.\n" 161"\n" 162" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 163" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 164" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 165" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" 166" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" 167" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" 168" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" 169" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" 170" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" 171" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" 172" SUCH DAMAGE."; 173#endif 174 175static int cflag; /* stdout mode */ 176static int dflag; /* decompress mode */ 177static int lflag; /* list mode */ 178static int numflag = 6; /* gzip -1..-9 value */ 179 180#ifndef SMALL 181static int fflag; /* force mode */ 182static int kflag; /* don't delete input files */ 183static int nflag; /* don't save name/timestamp */ 184static int Nflag; /* don't restore name/timestamp */ 185static int qflag; /* quiet mode */ 186static int rflag; /* recursive mode */ 187static int tflag; /* test */ 188static int vflag; /* verbose mode */ 189#else 190#define qflag 0 191#define tflag 0 192#endif 193 194static int exit_value = 0; /* exit value */ 195 196static char *infile; /* name of file coming in */ 197 198static void maybe_err(const char *fmt, ...) 199 __attribute__((__format__(__printf__, 1, 2))); 200#ifndef NO_BZIP2_SUPPORT 201static void maybe_errx(const char *fmt, ...) 202 __attribute__((__format__(__printf__, 1, 2))); 203#endif 204static void maybe_warn(const char *fmt, ...) 205 __attribute__((__format__(__printf__, 1, 2))); 206static void maybe_warnx(const char *fmt, ...) 207 __attribute__((__format__(__printf__, 1, 2))); 208static enum filetype file_gettype(u_char *); 209#ifdef SMALL 210#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) 211#endif 212static off_t gz_compress(int, int, off_t *, const char *, uint32_t); 213static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); 214static off_t file_compress(char *, char *, size_t); 215static off_t file_uncompress(char *, char *, size_t); 216static void handle_pathname(char *); 217static void handle_file(char *, struct stat *); 218static void handle_stdin(void); 219static void handle_stdout(void); 220static void print_ratio(off_t, off_t, FILE *); 221static void print_list(int fd, off_t, const char *, time_t); 222static void usage(void); 223static void display_version(void); 224#ifndef SMALL 225static void display_license(void); 226#endif 227static const suffixes_t *check_suffix(char *, int); 228static ssize_t read_retry(int, void *, size_t); 229 230#ifdef SMALL 231#define unlink_input(f, sb) unlink(f) 232#else 233static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); 234static void prepend_gzip(char *, int *, char ***); 235static void handle_dir(char *); 236static void print_verbage(const char *, const char *, off_t, off_t); 237static void print_test(const char *, int); 238static void copymodes(int fd, const struct stat *, const char *file); 239static int check_outfile(const char *outfile); 240#endif 241 242#ifndef NO_BZIP2_SUPPORT 243static off_t unbzip2(int, int, char *, size_t, off_t *); 244#endif 245 246#ifndef NO_COMPRESS_SUPPORT 247static FILE *zdopen(int); 248static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); 249#endif 250 251int main(int, char **p); 252 253#ifdef SMALL 254#define getopt_long(a,b,c,d,e) getopt(a,b,c) 255#else 256static const struct option longopts[] = { 257 { "stdout", no_argument, 0, 'c' }, 258 { "to-stdout", no_argument, 0, 'c' }, 259 { "decompress", no_argument, 0, 'd' }, 260 { "uncompress", no_argument, 0, 'd' }, 261 { "force", no_argument, 0, 'f' }, 262 { "help", no_argument, 0, 'h' }, 263 { "keep", no_argument, 0, 'k' }, 264 { "list", no_argument, 0, 'l' }, 265 { "no-name", no_argument, 0, 'n' }, 266 { "name", no_argument, 0, 'N' }, 267 { "quiet", no_argument, 0, 'q' }, 268 { "recursive", no_argument, 0, 'r' }, 269 { "suffix", required_argument, 0, 'S' }, 270 { "test", no_argument, 0, 't' }, 271 { "verbose", no_argument, 0, 'v' }, 272 { "version", no_argument, 0, 'V' }, 273 { "fast", no_argument, 0, '1' }, 274 { "best", no_argument, 0, '9' }, 275 { "ascii", no_argument, 0, 'a' }, 276 { "license", no_argument, 0, 'L' }, 277 { NULL, no_argument, 0, 0 }, 278}; 279#endif 280 281int 282main(int argc, char **argv) 283{ 284 const char *progname = getprogname(); 285#ifndef SMALL 286 char *gzip; 287 int len; 288#endif 289 int ch; 290 291 /* XXX set up signals */ 292 293#ifndef SMALL 294 if ((gzip = getenv("GZIP")) != NULL) 295 prepend_gzip(gzip, &argc, &argv); 296#endif 297 298 /* 299 * XXX 300 * handle being called `gunzip', `zcat' and `gzcat' 301 */ 302 if (strcmp(progname, "gunzip") == 0) 303 dflag = 1; 304 else if (strcmp(progname, "zcat") == 0 || 305 strcmp(progname, "gzcat") == 0) 306 dflag = cflag = 1; 307 308#ifdef SMALL 309#define OPT_LIST "123456789cdhltV" 310#else 311#define OPT_LIST "123456789acdfhklLNnqrS:tVv" 312#endif 313 314 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { 315 switch (ch) { 316 case '1': case '2': case '3': 317 case '4': case '5': case '6': 318 case '7': case '8': case '9': 319 numflag = ch - '0'; 320 break; 321 case 'c': 322 cflag = 1; 323 break; 324 case 'd': 325 dflag = 1; 326 break; 327 case 'l': 328 lflag = 1; 329 dflag = 1; 330 break; 331 case 'V': 332 display_version(); 333 /* NOTREACHED */ 334#ifndef SMALL 335 case 'a': 336 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname); 337 break; 338 case 'f': 339 fflag = 1; 340 break; 341 case 'k': 342 kflag = 1; 343 break; 344 case 'L': 345 display_license(); 346 /* NOT REACHED */ 347 case 'N': 348 nflag = 0; 349 Nflag = 1; 350 break; 351 case 'n': 352 nflag = 1; 353 Nflag = 0; 354 break; 355 case 'q': 356 qflag = 1; 357 break; 358 case 'r': 359 rflag = 1; 360 break; 361 case 'S': 362 len = strlen(optarg); 363 if (len != 0) { 364 suffixes[0].zipped = optarg; 365 suffixes[0].ziplen = len; 366 } else { 367 suffixes[NUM_SUFFIXES - 1].zipped = ""; 368 suffixes[NUM_SUFFIXES - 1].ziplen = 0; 369 } 370 break; 371 case 't': 372 cflag = 1; 373 tflag = 1; 374 dflag = 1; 375 break; 376 case 'v': 377 vflag = 1; 378 break; 379#endif 380 default: 381 usage(); 382 /* NOTREACHED */ 383 } 384 } 385 argv += optind; 386 argc -= optind; 387 388 if (argc == 0) { 389 if (dflag) /* stdin mode */ 390 handle_stdin(); 391 else /* stdout mode */ 392 handle_stdout(); 393 } else { 394 do { 395 handle_pathname(argv[0]); 396 } while (*++argv); 397 } 398#ifndef SMALL 399 if (qflag == 0 && lflag && argc > 1) 400 print_list(-1, 0, "(totals)", 0); 401#endif 402 exit(exit_value); 403} 404 405/* maybe print a warning */ 406void 407maybe_warn(const char *fmt, ...) 408{ 409 va_list ap; 410 411 if (qflag == 0) { 412 va_start(ap, fmt); 413 vwarn(fmt, ap); 414 va_end(ap); 415 } 416 if (exit_value == 0) 417 exit_value = 1; 418} 419 420/* ... without an errno. */ 421void 422maybe_warnx(const char *fmt, ...) 423{ 424 va_list ap; 425 426 if (qflag == 0) { 427 va_start(ap, fmt); 428 vwarnx(fmt, ap); 429 va_end(ap); 430 } 431 if (exit_value == 0) 432 exit_value = 1; 433} 434 435/* maybe print an error */ 436void 437maybe_err(const char *fmt, ...) 438{ 439 va_list ap; 440 441 if (qflag == 0) { 442 va_start(ap, fmt); 443 vwarn(fmt, ap); 444 va_end(ap); 445 } 446 exit(2); 447} 448 449#ifndef NO_BZIP2_SUPPORT 450/* ... without an errno. */ 451void 452maybe_errx(const char *fmt, ...) 453{ 454 va_list ap; 455 456 if (qflag == 0) { 457 va_start(ap, fmt); 458 vwarnx(fmt, ap); 459 va_end(ap); 460 } 461 exit(2); 462} 463#endif 464 465#ifndef SMALL 466/* split up $GZIP and prepend it to the argument list */ 467static void 468prepend_gzip(char *gzip, int *argc, char ***argv) 469{ 470 char *s, **nargv, **ac; 471 int nenvarg = 0, i; 472 473 /* scan how many arguments there are */ 474 for (s = gzip;;) { 475 while (*s == ' ' || *s == '\t') 476 s++; 477 if (*s == 0) 478 goto count_done; 479 nenvarg++; 480 while (*s != ' ' && *s != '\t') 481 if (*s++ == 0) 482 goto count_done; 483 } 484count_done: 485 /* punt early */ 486 if (nenvarg == 0) 487 return; 488 489 *argc += nenvarg; 490 ac = *argv; 491 492 nargv = (char **)malloc((*argc + 1) * sizeof(char *)); 493 if (nargv == NULL) 494 maybe_err("malloc"); 495 496 /* stash this away */ 497 *argv = nargv; 498 499 /* copy the program name first */ 500 i = 0; 501 nargv[i++] = *(ac++); 502 503 /* take a copy of $GZIP and add it to the array */ 504 s = strdup(gzip); 505 if (s == NULL) 506 maybe_err("strdup"); 507 for (;;) { 508 /* Skip whitespaces. */ 509 while (*s == ' ' || *s == '\t') 510 s++; 511 if (*s == 0) 512 goto copy_done; 513 nargv[i++] = s; 514 /* Find the end of this argument. */ 515 while (*s != ' ' && *s != '\t') 516 if (*s++ == 0) 517 /* Argument followed by NUL. */ 518 goto copy_done; 519 /* Terminate by overwriting ' ' or '\t' with NUL. */ 520 *s++ = 0; 521 } 522copy_done: 523 524 /* copy the original arguments and a NULL */ 525 while (*ac) 526 nargv[i++] = *(ac++); 527 nargv[i] = NULL; 528} 529#endif 530 531/* compress input to output. Return bytes read, -1 on error */ 532static off_t 533gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) 534{ 535 z_stream z; 536 char *outbufp, *inbufp; 537 off_t in_tot = 0, out_tot = 0; 538 ssize_t in_size; 539 int i, error; 540 uLong crc; 541#ifdef SMALL 542 static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, 543 0, 0, 0, 0, 544 0, OS_CODE }; 545#endif 546 547 outbufp = malloc(BUFLEN); 548 inbufp = malloc(BUFLEN); 549 if (outbufp == NULL || inbufp == NULL) { 550 maybe_err("malloc failed"); 551 goto out; 552 } 553 554 memset(&z, 0, sizeof z); 555 z.zalloc = Z_NULL; 556 z.zfree = Z_NULL; 557 z.opaque = 0; 558 559#ifdef SMALL 560 memcpy(outbufp, header, sizeof header); 561 i = sizeof header; 562#else 563 if (nflag != 0) { 564 mtime = 0; 565 origname = ""; 566 } 567 568 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", 569 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 570 *origname ? ORIG_NAME : 0, 571 mtime & 0xff, 572 (mtime >> 8) & 0xff, 573 (mtime >> 16) & 0xff, 574 (mtime >> 24) & 0xff, 575 numflag == 1 ? 4 : numflag == 9 ? 2 : 0, 576 OS_CODE, origname); 577 if (i >= BUFLEN) 578 /* this need PATH_MAX > BUFLEN ... */ 579 maybe_err("snprintf"); 580 if (*origname) 581 i++; 582#endif 583 584 z.next_out = (unsigned char *)outbufp + i; 585 z.avail_out = BUFLEN - i; 586 587 error = deflateInit2(&z, numflag, Z_DEFLATED, 588 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); 589 if (error != Z_OK) { 590 maybe_warnx("deflateInit2 failed"); 591 in_tot = -1; 592 goto out; 593 } 594 595 crc = crc32(0L, Z_NULL, 0); 596 for (;;) { 597 if (z.avail_out == 0) { 598 if (write(out, outbufp, BUFLEN) != BUFLEN) { 599 maybe_warn("write"); 600 out_tot = -1; 601 goto out; 602 } 603 604 out_tot += BUFLEN; 605 z.next_out = (unsigned char *)outbufp; 606 z.avail_out = BUFLEN; 607 } 608 609 if (z.avail_in == 0) { 610 in_size = read(in, inbufp, BUFLEN); 611 if (in_size < 0) { 612 maybe_warn("read"); 613 in_tot = -1; 614 goto out; 615 } 616 if (in_size == 0) 617 break; 618 619 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); 620 in_tot += in_size; 621 z.next_in = (unsigned char *)inbufp; 622 z.avail_in = in_size; 623 } 624 625 error = deflate(&z, Z_NO_FLUSH); 626 if (error != Z_OK && error != Z_STREAM_END) { 627 maybe_warnx("deflate failed"); 628 in_tot = -1; 629 goto out; 630 } 631 } 632 633 /* clean up */ 634 for (;;) { 635 size_t len; 636 ssize_t w; 637 638 error = deflate(&z, Z_FINISH); 639 if (error != Z_OK && error != Z_STREAM_END) { 640 maybe_warnx("deflate failed"); 641 in_tot = -1; 642 goto out; 643 } 644 645 len = (char *)z.next_out - outbufp; 646 647 w = write(out, outbufp, len); 648 if (w == -1 || (size_t)w != len) { 649 maybe_warn("write"); 650 out_tot = -1; 651 goto out; 652 } 653 out_tot += len; 654 z.next_out = (unsigned char *)outbufp; 655 z.avail_out = BUFLEN; 656 657 if (error == Z_STREAM_END) 658 break; 659 } 660 661 if (deflateEnd(&z) != Z_OK) { 662 maybe_warnx("deflateEnd failed"); 663 in_tot = -1; 664 goto out; 665 } 666 667 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 668 (int)crc & 0xff, 669 (int)(crc >> 8) & 0xff, 670 (int)(crc >> 16) & 0xff, 671 (int)(crc >> 24) & 0xff, 672 (int)in_tot & 0xff, 673 (int)(in_tot >> 8) & 0xff, 674 (int)(in_tot >> 16) & 0xff, 675 (int)(in_tot >> 24) & 0xff); 676 if (i != 8) 677 maybe_err("snprintf"); 678 if (write(out, outbufp, i) != i) { 679 maybe_warn("write"); 680 in_tot = -1; 681 } else 682 out_tot += i; 683 684out: 685 if (inbufp != NULL) 686 free(inbufp); 687 if (outbufp != NULL) 688 free(outbufp); 689 if (gsizep) 690 *gsizep = out_tot; 691 return in_tot; 692} 693 694/* 695 * uncompress input to output then close the input. return the 696 * uncompressed size written, and put the compressed sized read 697 * into `*gsizep'. 698 */ 699static off_t 700gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, 701 const char *filename) 702{ 703 z_stream z; 704 char *outbufp, *inbufp; 705 off_t out_tot = -1, in_tot = 0; 706 uint32_t out_sub_tot = 0; 707 enum { 708 GZSTATE_MAGIC0, 709 GZSTATE_MAGIC1, 710 GZSTATE_METHOD, 711 GZSTATE_FLAGS, 712 GZSTATE_SKIPPING, 713 GZSTATE_EXTRA, 714 GZSTATE_EXTRA2, 715 GZSTATE_EXTRA3, 716 GZSTATE_ORIGNAME, 717 GZSTATE_COMMENT, 718 GZSTATE_HEAD_CRC1, 719 GZSTATE_HEAD_CRC2, 720 GZSTATE_INIT, 721 GZSTATE_READ, 722 GZSTATE_CRC, 723 GZSTATE_LEN, 724 } state = GZSTATE_MAGIC0; 725 int flags = 0, skip_count = 0; 726 int error = Z_STREAM_ERROR, done_reading = 0; 727 uLong crc = 0; 728 ssize_t wr; 729 int needmore = 0; 730 731#define ADVANCE() { z.next_in++; z.avail_in--; } 732 733 if ((outbufp = malloc(BUFLEN)) == NULL) { 734 maybe_err("malloc failed"); 735 goto out2; 736 } 737 if ((inbufp = malloc(BUFLEN)) == NULL) { 738 maybe_err("malloc failed"); 739 goto out1; 740 } 741 742 memset(&z, 0, sizeof z); 743 z.avail_in = prelen; 744 z.next_in = (unsigned char *)pre; 745 z.avail_out = BUFLEN; 746 z.next_out = (unsigned char *)outbufp; 747 z.zalloc = NULL; 748 z.zfree = NULL; 749 z.opaque = 0; 750 751 in_tot = prelen; 752 out_tot = 0; 753 754 for (;;) { 755 if ((z.avail_in == 0 || needmore) && done_reading == 0) { 756 ssize_t in_size; 757 758 if (z.avail_in > 0) { 759 memmove(inbufp, z.next_in, z.avail_in); 760 } 761 z.next_in = (unsigned char *)inbufp; 762 in_size = read(in, z.next_in + z.avail_in, 763 BUFLEN - z.avail_in); 764 765 if (in_size == -1) { 766 maybe_warn("failed to read stdin"); 767 goto stop_and_fail; 768 } else if (in_size == 0) { 769 done_reading = 1; 770 } 771 772 z.avail_in += in_size; 773 needmore = 0; 774 775 in_tot += in_size; 776 } 777 if (z.avail_in == 0) { 778 if (done_reading && state != GZSTATE_MAGIC0) { 779 maybe_warnx("%s: unexpected end of file", 780 filename); 781 goto stop_and_fail; 782 } 783 goto stop; 784 } 785 switch (state) { 786 case GZSTATE_MAGIC0: 787 if (*z.next_in != GZIP_MAGIC0) { 788 if (in_tot > 0) { 789 maybe_warnx("%s: trailing garbage " 790 "ignored", filename); 791 goto stop; 792 } 793 maybe_warnx("input not gziped (MAGIC0)"); 794 goto stop_and_fail; 795 } 796 ADVANCE(); 797 state++; 798 out_sub_tot = 0; 799 crc = crc32(0L, Z_NULL, 0); 800 break; 801 802 case GZSTATE_MAGIC1: 803 if (*z.next_in != GZIP_MAGIC1 && 804 *z.next_in != GZIP_OMAGIC1) { 805 maybe_warnx("input not gziped (MAGIC1)"); 806 goto stop_and_fail; 807 } 808 ADVANCE(); 809 state++; 810 break; 811 812 case GZSTATE_METHOD: 813 if (*z.next_in != Z_DEFLATED) { 814 maybe_warnx("unknown compression method"); 815 goto stop_and_fail; 816 } 817 ADVANCE(); 818 state++; 819 break; 820 821 case GZSTATE_FLAGS: 822 flags = *z.next_in; 823 ADVANCE(); 824 skip_count = 6; 825 state++; 826 break; 827 828 case GZSTATE_SKIPPING: 829 if (skip_count > 0) { 830 skip_count--; 831 ADVANCE(); 832 } else 833 state++; 834 break; 835 836 case GZSTATE_EXTRA: 837 if ((flags & EXTRA_FIELD) == 0) { 838 state = GZSTATE_ORIGNAME; 839 break; 840 } 841 skip_count = *z.next_in; 842 ADVANCE(); 843 state++; 844 break; 845 846 case GZSTATE_EXTRA2: 847 skip_count |= ((*z.next_in) << 8); 848 ADVANCE(); 849 state++; 850 break; 851 852 case GZSTATE_EXTRA3: 853 if (skip_count > 0) { 854 skip_count--; 855 ADVANCE(); 856 } else 857 state++; 858 break; 859 860 case GZSTATE_ORIGNAME: 861 if ((flags & ORIG_NAME) == 0) { 862 state++; 863 break; 864 } 865 if (*z.next_in == 0) 866 state++; 867 ADVANCE(); 868 break; 869 870 case GZSTATE_COMMENT: 871 if ((flags & COMMENT) == 0) { 872 state++; 873 break; 874 } 875 if (*z.next_in == 0) 876 state++; 877 ADVANCE(); 878 break; 879 880 case GZSTATE_HEAD_CRC1: 881 if (flags & HEAD_CRC) 882 skip_count = 2; 883 else 884 skip_count = 0; 885 state++; 886 break; 887 888 case GZSTATE_HEAD_CRC2: 889 if (skip_count > 0) { 890 skip_count--; 891 ADVANCE(); 892 } else 893 state++; 894 break; 895 896 case GZSTATE_INIT: 897 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 898 maybe_warnx("failed to inflateInit"); 899 goto stop_and_fail; 900 } 901 state++; 902 break; 903 904 case GZSTATE_READ: 905 error = inflate(&z, Z_FINISH); 906 switch (error) { 907 /* Z_BUF_ERROR goes with Z_FINISH... */ 908 case Z_BUF_ERROR: 909 case Z_STREAM_END: 910 case Z_OK: 911 break; 912 913 case Z_NEED_DICT: 914 maybe_warnx("Z_NEED_DICT error"); 915 goto stop_and_fail; 916 case Z_DATA_ERROR: 917 maybe_warnx("data stream error"); 918 goto stop_and_fail; 919 case Z_STREAM_ERROR: 920 maybe_warnx("internal stream error"); 921 goto stop_and_fail; 922 case Z_MEM_ERROR: 923 maybe_warnx("memory allocation error"); 924 goto stop_and_fail; 925 926 default: 927 maybe_warn("unknown error from inflate(): %d", 928 error); 929 } 930 wr = BUFLEN - z.avail_out; 931 932 if (wr != 0) { 933 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); 934 if ( 935#ifndef SMALL 936 /* don't write anything with -t */ 937 tflag == 0 && 938#endif 939 write(out, outbufp, wr) != wr) { 940 maybe_warn("error writing to output"); 941 goto stop_and_fail; 942 } 943 944 out_tot += wr; 945 out_sub_tot += wr; 946 } 947 948 if (error == Z_STREAM_END) { 949 inflateEnd(&z); 950 state++; 951 } 952 953 z.next_out = (unsigned char *)outbufp; 954 z.avail_out = BUFLEN; 955 956 break; 957 case GZSTATE_CRC: 958 { 959 uLong origcrc; 960 961 if (z.avail_in < 4) { 962 if (!done_reading) { 963 needmore = 1; 964 continue; 965 } 966 maybe_warnx("truncated input"); 967 goto stop_and_fail; 968 } 969 origcrc = ((unsigned)z.next_in[0] & 0xff) | 970 ((unsigned)z.next_in[1] & 0xff) << 8 | 971 ((unsigned)z.next_in[2] & 0xff) << 16 | 972 ((unsigned)z.next_in[3] & 0xff) << 24; 973 if (origcrc != crc) { 974 maybe_warnx("invalid compressed" 975 " data--crc error"); 976 goto stop_and_fail; 977 } 978 } 979 980 z.avail_in -= 4; 981 z.next_in += 4; 982 983 if (!z.avail_in && done_reading) { 984 goto stop; 985 } 986 state++; 987 break; 988 case GZSTATE_LEN: 989 { 990 uLong origlen; 991 992 if (z.avail_in < 4) { 993 if (!done_reading) { 994 needmore = 1; 995 continue; 996 } 997 maybe_warnx("truncated input"); 998 goto stop_and_fail; 999 } 1000 origlen = ((unsigned)z.next_in[0] & 0xff) | 1001 ((unsigned)z.next_in[1] & 0xff) << 8 | 1002 ((unsigned)z.next_in[2] & 0xff) << 16 | 1003 ((unsigned)z.next_in[3] & 0xff) << 24; 1004 1005 if (origlen != out_sub_tot) { 1006 maybe_warnx("invalid compressed" 1007 " data--length error"); 1008 goto stop_and_fail; 1009 } 1010 } 1011 1012 z.avail_in -= 4; 1013 z.next_in += 4; 1014 1015 if (error < 0) { 1016 maybe_warnx("decompression error"); 1017 goto stop_and_fail; 1018 } 1019 state = GZSTATE_MAGIC0; 1020 break; 1021 } 1022 continue; 1023stop_and_fail: 1024 out_tot = -1; 1025stop: 1026 break; 1027 } 1028 if (state > GZSTATE_INIT) 1029 inflateEnd(&z); 1030 1031 free(inbufp); 1032out1: 1033 free(outbufp); 1034out2: 1035 if (gsizep) 1036 *gsizep = in_tot; 1037 return (out_tot); 1038} 1039 1040#ifndef SMALL 1041/* 1042 * set the owner, mode, flags & utimes using the given file descriptor. 1043 * file is only used in possible warning messages. 1044 */ 1045static void 1046copymodes(int fd, const struct stat *sbp, const char *file) 1047{ 1048 struct timeval times[2]; 1049 struct stat sb; 1050 1051 /* 1052 * If we have no info on the input, give this file some 1053 * default values and return.. 1054 */ 1055 if (sbp == NULL) { 1056 mode_t mask = umask(022); 1057 1058 (void)fchmod(fd, DEFFILEMODE & ~mask); 1059 (void)umask(mask); 1060 return; 1061 } 1062 sb = *sbp; 1063 1064 /* if the chown fails, remove set-id bits as-per compress(1) */ 1065 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { 1066 if (errno != EPERM) 1067 maybe_warn("couldn't fchown: %s", file); 1068 sb.st_mode &= ~(S_ISUID|S_ISGID); 1069 } 1070 1071 /* we only allow set-id and the 9 normal permission bits */ 1072 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 1073 if (fchmod(fd, sb.st_mode) < 0) 1074 maybe_warn("couldn't fchmod: %s", file); 1075 1076 TIMESPEC_TO_TIMEVAL(×[0], &sb.st_atimespec); 1077 TIMESPEC_TO_TIMEVAL(×[1], &sb.st_mtimespec); 1078 if (futimes(fd, times) < 0) 1079 maybe_warn("couldn't utimes: %s", file); 1080 1081 /* only try flags if they exist already */ 1082 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) 1083 maybe_warn("couldn't fchflags: %s", file); 1084} 1085#endif 1086 1087/* what sort of file is this? */ 1088static enum filetype 1089file_gettype(u_char *buf) 1090{ 1091 1092 if (buf[0] == GZIP_MAGIC0 && 1093 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 1094 return FT_GZIP; 1095 else 1096#ifndef NO_BZIP2_SUPPORT 1097 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 1098 buf[3] >= '0' && buf[3] <= '9') 1099 return FT_BZIP2; 1100 else 1101#endif 1102#ifndef NO_COMPRESS_SUPPORT 1103 if (memcmp(buf, Z_MAGIC, 2) == 0) 1104 return FT_Z; 1105 else 1106#endif 1107 return FT_UNKNOWN; 1108} 1109 1110#ifndef SMALL 1111/* check the outfile is OK. */ 1112static int 1113check_outfile(const char *outfile) 1114{ 1115 struct stat sb; 1116 int ok = 1; 1117 1118 if (lflag == 0 && stat(outfile, &sb) == 0) { 1119 if (fflag) 1120 unlink(outfile); 1121 else if (isatty(STDIN_FILENO)) { 1122 char ans[10] = { 'n', '\0' }; /* default */ 1123 1124 fprintf(stderr, "%s already exists -- do you wish to " 1125 "overwrite (y or n)? " , outfile); 1126 (void)fgets(ans, sizeof(ans) - 1, stdin); 1127 if (ans[0] != 'y' && ans[0] != 'Y') { 1128 fprintf(stderr, "\tnot overwriting\n"); 1129 ok = 0; 1130 } else 1131 unlink(outfile); 1132 } else { 1133 maybe_warnx("%s already exists -- skipping", outfile); 1134 ok = 0; 1135 } 1136 } 1137 return ok; 1138} 1139 1140static void 1141unlink_input(const char *file, const struct stat *sb) 1142{ 1143 struct stat nsb; 1144 1145 if (kflag) 1146 return; 1147 if (stat(file, &nsb) != 0) 1148 /* Must be gone alrady */ 1149 return; 1150 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) 1151 /* Definitely a different file */ 1152 return; 1153 unlink(file); 1154} 1155#endif 1156 1157static const suffixes_t * 1158check_suffix(char *file, int xlate) 1159{ 1160 const suffixes_t *s; 1161 int len = strlen(file); 1162 char *sp; 1163 1164 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { 1165 /* if it doesn't fit in "a.suf", don't bother */ 1166 if (s->ziplen >= len) 1167 continue; 1168 sp = file + len - s->ziplen; 1169 if (strcmp(s->zipped, sp) != 0) 1170 continue; 1171 if (xlate) 1172 strcpy(sp, s->normal); 1173 return s; 1174 } 1175 return NULL; 1176} 1177 1178/* 1179 * compress the given file: create a corresponding .gz file and remove the 1180 * original. 1181 */ 1182static off_t 1183file_compress(char *file, char *outfile, size_t outsize) 1184{ 1185 int in; 1186 int out; 1187 off_t size, insize; 1188#ifndef SMALL 1189 struct stat isb, osb; 1190 const suffixes_t *suff; 1191#endif 1192 1193 in = open(file, O_RDONLY); 1194 if (in == -1) { 1195 maybe_warn("can't open %s", file); 1196 return -1; 1197 } 1198 1199 if (cflag == 0) { 1200#ifndef SMALL 1201 if (fstat(in, &isb) == 0) { 1202 if (isb.st_nlink > 1 && fflag == 0) { 1203 maybe_warnx("%s has %d other link%s -- " 1204 "skipping", file, isb.st_nlink - 1, 1205 isb.st_nlink == 1 ? "" : "s"); 1206 close(in); 1207 return -1; 1208 } 1209 } 1210 1211 if (fflag == 0 && (suff = check_suffix(file, 0)) 1212 && suff->zipped[0] != 0) { 1213 maybe_warnx("%s already has %s suffix -- unchanged", 1214 file, suff->zipped); 1215 close(in); 1216 return -1; 1217 } 1218#endif 1219 1220 /* Add (usually) .gz to filename */ 1221 if ((size_t)snprintf(outfile, outsize, "%s%s", 1222 file, suffixes[0].zipped) >= outsize) 1223 memcpy(outfile - suffixes[0].ziplen - 1, 1224 suffixes[0].zipped, suffixes[0].ziplen + 1); 1225 1226#ifndef SMALL 1227 if (check_outfile(outfile) == 0) { 1228 close(in); 1229 return -1; 1230 } 1231#endif 1232 } 1233 1234 if (cflag == 0) { 1235 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); 1236 if (out == -1) { 1237 maybe_warn("could not create output: %s", outfile); 1238 fclose(stdin); 1239 return -1; 1240 } 1241 } else 1242 out = STDOUT_FILENO; 1243 1244 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); 1245 1246 (void)close(in); 1247 1248 /* 1249 * If there was an error, insize will be -1. 1250 * If we compressed to stdout, just return the size. 1251 * Otherwise stat the file and check it is the correct size. 1252 * We only blow away the file if we can stat the output and it 1253 * has the expected size. 1254 */ 1255 if (cflag != 0) 1256 return insize == -1 ? -1 : size; 1257 1258#ifndef SMALL 1259 if (fstat(out, &osb) != 0) { 1260 maybe_warn("couldn't stat: %s", outfile); 1261 goto bad_outfile; 1262 } 1263 1264 if (osb.st_size != size) { 1265 maybe_warnx("output file: %s wrong size (%" PRIdOFF 1266 " != %" PRIdOFF "), deleting", 1267 outfile, osb.st_size, size); 1268 goto bad_outfile; 1269 } 1270 1271 copymodes(out, &isb, outfile); 1272#endif 1273 if (close(out) == -1) 1274 maybe_warn("couldn't close output"); 1275 1276 /* output is good, ok to delete input */ 1277 unlink_input(file, &isb); 1278 return size; 1279 1280#ifndef SMALL 1281 bad_outfile: 1282 if (close(out) == -1) 1283 maybe_warn("couldn't close output"); 1284 1285 maybe_warnx("leaving original %s", file); 1286 unlink(outfile); 1287 return size; 1288#endif 1289} 1290 1291/* uncompress the given file and remove the original */ 1292static off_t 1293file_uncompress(char *file, char *outfile, size_t outsize) 1294{ 1295 struct stat isb, osb; 1296 off_t size; 1297 ssize_t rbytes; 1298 unsigned char header1[4]; 1299 enum filetype method; 1300 int rv, fd, ofd, zfd = -1; 1301#ifndef SMALL 1302 time_t timestamp = 0; 1303 unsigned char name[PATH_MAX + 1]; 1304#endif 1305 1306 /* gather the old name info */ 1307 1308 fd = open(file, O_RDONLY); 1309 if (fd < 0) { 1310 maybe_warn("can't open %s", file); 1311 goto lose; 1312 } 1313 1314 strlcpy(outfile, file, outsize); 1315 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1316 maybe_warnx("%s: unknown suffix -- ignored", file); 1317 goto lose; 1318 } 1319 1320 rbytes = read(fd, header1, sizeof header1); 1321 if (rbytes != sizeof header1) { 1322 /* we don't want to fail here. */ 1323#ifndef SMALL 1324 if (fflag) 1325 goto lose; 1326#endif 1327 if (rbytes == -1) 1328 maybe_warn("can't read %s", file); 1329 else 1330 goto unexpected_EOF; 1331 goto lose; 1332 } 1333 1334 method = file_gettype(header1); 1335 1336#ifndef SMALL 1337 if (fflag == 0 && method == FT_UNKNOWN) { 1338 maybe_warnx("%s: not in gzip format", file); 1339 goto lose; 1340 } 1341 1342#endif 1343 1344#ifndef SMALL 1345 if (method == FT_GZIP && Nflag) { 1346 unsigned char ts[4]; /* timestamp */ 1347 1348 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1349 if (rv >= 0 && (size_t)rv < sizeof ts) 1350 goto unexpected_EOF; 1351 if (rv == -1) { 1352 if (!fflag) 1353 maybe_warn("can't read %s", file); 1354 goto lose; 1355 } 1356 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; 1357 1358 if (header1[3] & ORIG_NAME) { 1359 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME); 1360 if (rbytes < 0) { 1361 maybe_warn("can't read %s", file); 1362 goto lose; 1363 } 1364 if (name[0] != 0) { 1365 /* preserve original directory name */ 1366 char *dp = strrchr(file, '/'); 1367 if (dp == NULL) 1368 dp = file; 1369 else 1370 dp++; 1371 snprintf(outfile, outsize, "%.*s%.*s", 1372 (int) (dp - file), 1373 file, (int) rbytes, name); 1374 } 1375 } 1376 } 1377#endif 1378 lseek(fd, 0, SEEK_SET); 1379 1380 if (cflag == 0 || lflag) { 1381 if (fstat(fd, &isb) != 0) 1382 goto lose; 1383#ifndef SMALL 1384 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1385 maybe_warnx("%s has %d other links -- skipping", 1386 file, isb.st_nlink - 1); 1387 goto lose; 1388 } 1389 if (nflag == 0 && timestamp) 1390 isb.st_mtime = timestamp; 1391 if (check_outfile(outfile) == 0) 1392 goto lose; 1393#endif 1394 } 1395 1396 if (cflag == 0 && lflag == 0) { 1397 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1398 if (zfd == STDOUT_FILENO) { 1399 /* We won't close STDOUT_FILENO later... */ 1400 zfd = dup(zfd); 1401 close(STDOUT_FILENO); 1402 } 1403 if (zfd == -1) { 1404 maybe_warn("can't open %s", outfile); 1405 goto lose; 1406 } 1407 } else 1408 zfd = STDOUT_FILENO; 1409 1410#ifndef NO_BZIP2_SUPPORT 1411 if (method == FT_BZIP2) { 1412 1413 /* XXX */ 1414 if (lflag) { 1415 maybe_warnx("no -l with bzip2 files"); 1416 goto lose; 1417 } 1418 1419 size = unbzip2(fd, zfd, NULL, 0, NULL); 1420 } else 1421#endif 1422 1423#ifndef NO_COMPRESS_SUPPORT 1424 if (method == FT_Z) { 1425 FILE *in, *out; 1426 1427 /* XXX */ 1428 if (lflag) { 1429 maybe_warnx("no -l with Lempel-Ziv files"); 1430 goto lose; 1431 } 1432 1433 if ((in = zdopen(fd)) == NULL) { 1434 maybe_warn("zdopen for read: %s", file); 1435 goto lose; 1436 } 1437 1438 out = fdopen(dup(zfd), "w"); 1439 if (out == NULL) { 1440 maybe_warn("fdopen for write: %s", outfile); 1441 fclose(in); 1442 goto lose; 1443 } 1444 1445 size = zuncompress(in, out, NULL, 0, NULL); 1446 /* need to fclose() if ferror() is true... */ 1447 if (ferror(in) | fclose(in)) { 1448 maybe_warn("failed infile fclose"); 1449 unlink(outfile); 1450 (void)fclose(out); 1451 } 1452 if (fclose(out) != 0) { 1453 maybe_warn("failed outfile fclose"); 1454 unlink(outfile); 1455 goto lose; 1456 } 1457 } else 1458#endif 1459 1460#ifndef SMALL 1461 if (method == FT_UNKNOWN) { 1462 if (lflag) { 1463 maybe_warnx("no -l for unknown filetypes"); 1464 goto lose; 1465 } 1466 size = cat_fd(NULL, 0, NULL, fd); 1467 } else 1468#endif 1469 { 1470 if (lflag) { 1471 print_list(fd, isb.st_size, outfile, isb.st_mtime); 1472 close(fd); 1473 return -1; /* XXX */ 1474 } 1475 1476 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1477 } 1478 1479 if (close(fd) != 0) 1480 maybe_warn("couldn't close input"); 1481 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1482 maybe_warn("couldn't close output"); 1483 1484 if (size == -1) { 1485 if (cflag == 0) 1486 unlink(outfile); 1487 maybe_warnx("%s: uncompress failed", file); 1488 return -1; 1489 } 1490 1491 /* if testing, or we uncompressed to stdout, this is all we need */ 1492#ifndef SMALL 1493 if (tflag) 1494 return size; 1495#endif 1496 /* if we are uncompressing to stdin, don't remove the file. */ 1497 if (cflag) 1498 return size; 1499 1500 /* 1501 * if we create a file... 1502 */ 1503 /* 1504 * if we can't stat the file don't remove the file. 1505 */ 1506 1507 ofd = open(outfile, O_RDWR, 0); 1508 if (ofd == -1) { 1509 maybe_warn("couldn't open (leaving original): %s", 1510 outfile); 1511 return -1; 1512 } 1513 if (fstat(ofd, &osb) != 0) { 1514 maybe_warn("couldn't stat (leaving original): %s", 1515 outfile); 1516 close(ofd); 1517 return -1; 1518 } 1519 if (osb.st_size != size) { 1520 maybe_warnx("stat gave different size: %" PRIdOFF 1521 " != %" PRIdOFF " (leaving original)", 1522 size, osb.st_size); 1523 close(ofd); 1524 unlink(outfile); 1525 return -1; 1526 } 1527 unlink_input(file, &isb); 1528#ifndef SMALL 1529 copymodes(ofd, &isb, outfile); 1530#endif 1531 close(ofd); 1532 return size; 1533 1534 unexpected_EOF: 1535 maybe_warnx("%s: unexpected end of file", file); 1536 lose: 1537 if (fd != -1) 1538 close(fd); 1539 if (zfd != -1 && zfd != STDOUT_FILENO) 1540 close(fd); 1541 return -1; 1542} 1543 1544#ifndef SMALL 1545static off_t 1546cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1547{ 1548 char buf[BUFLEN]; 1549 off_t in_tot; 1550 ssize_t w; 1551 1552 in_tot = count; 1553 w = write(STDOUT_FILENO, prepend, count); 1554 if (w == -1 || (size_t)w != count) { 1555 maybe_warn("write to stdout"); 1556 return -1; 1557 } 1558 for (;;) { 1559 ssize_t rv; 1560 1561 rv = read(fd, buf, sizeof buf); 1562 if (rv == 0) 1563 break; 1564 if (rv < 0) { 1565 maybe_warn("read from fd %d", fd); 1566 break; 1567 } 1568 1569 if (write(STDOUT_FILENO, buf, rv) != rv) { 1570 maybe_warn("write to stdout"); 1571 break; 1572 } 1573 in_tot += rv; 1574 } 1575 1576 if (gsizep) 1577 *gsizep = in_tot; 1578 return (in_tot); 1579} 1580#endif 1581 1582static void 1583handle_stdin(void) 1584{ 1585 unsigned char header1[4]; 1586 off_t usize, gsize; 1587 enum filetype method; 1588 ssize_t bytes_read; 1589#ifndef NO_COMPRESS_SUPPORT 1590 FILE *in; 1591#endif 1592 1593#ifndef SMALL 1594 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1595 maybe_warnx("standard input is a terminal -- ignoring"); 1596 return; 1597 } 1598#endif 1599 1600 if (lflag) { 1601 struct stat isb; 1602 1603 /* XXX could read the whole file, etc. */ 1604 if (fstat(STDIN_FILENO, &isb) < 0) { 1605 maybe_warn("fstat"); 1606 return; 1607 } 1608 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1609 return; 1610 } 1611 1612 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1); 1613 if (bytes_read == -1) { 1614 maybe_warn("can't read stdin"); 1615 return; 1616 } else if (bytes_read != sizeof(header1)) { 1617 maybe_warnx("(stdin): unexpected end of file"); 1618 return; 1619 } 1620 1621 method = file_gettype(header1); 1622 switch (method) { 1623 default: 1624#ifndef SMALL 1625 if (fflag == 0) { 1626 maybe_warnx("unknown compression format"); 1627 return; 1628 } 1629 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO); 1630 break; 1631#endif 1632 case FT_GZIP: 1633 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1634 (char *)header1, sizeof header1, &gsize, "(stdin)"); 1635 break; 1636#ifndef NO_BZIP2_SUPPORT 1637 case FT_BZIP2: 1638 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1639 (char *)header1, sizeof header1, &gsize); 1640 break; 1641#endif 1642#ifndef NO_COMPRESS_SUPPORT 1643 case FT_Z: 1644 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1645 maybe_warnx("zopen of stdin"); 1646 return; 1647 } 1648 1649 usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize); 1650 fclose(in); 1651 break; 1652#endif 1653 } 1654 1655#ifndef SMALL 1656 if (vflag && !tflag && usize != -1 && gsize != -1) 1657 print_verbage(NULL, NULL, usize, gsize); 1658 if (vflag && tflag) 1659 print_test("(stdin)", usize != -1); 1660#endif 1661 1662} 1663 1664static void 1665handle_stdout(void) 1666{ 1667 off_t gsize, usize; 1668 struct stat sb; 1669 time_t systime; 1670 uint32_t mtime; 1671 int ret; 1672 1673#ifndef SMALL 1674 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1675 maybe_warnx("standard output is a terminal -- ignoring"); 1676 return; 1677 } 1678#endif 1679 /* If stdin is a file use it's mtime, otherwise use current time */ 1680 ret = fstat(STDIN_FILENO, &sb); 1681 1682#ifndef SMALL 1683 if (ret < 0) { 1684 maybe_warn("Can't stat stdin"); 1685 return; 1686 } 1687#endif 1688 1689 if (S_ISREG(sb.st_mode)) 1690 mtime = (uint32_t)sb.st_mtime; 1691 else { 1692 systime = time(NULL); 1693#ifndef SMALL 1694 if (systime == -1) { 1695 maybe_warn("time"); 1696 return; 1697 } 1698#endif 1699 mtime = (uint32_t)systime; 1700 } 1701 1702 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1703#ifndef SMALL 1704 if (vflag && !tflag && usize != -1 && gsize != -1) 1705 print_verbage(NULL, NULL, usize, gsize); 1706#endif 1707} 1708 1709/* do what is asked for, for the path name */ 1710static void 1711handle_pathname(char *path) 1712{ 1713 char *opath = path, *s = NULL; 1714 ssize_t len; 1715 int slen; 1716 struct stat sb; 1717 1718 /* check for stdout/stdin */ 1719 if (path[0] == '-' && path[1] == '\0') { 1720 if (dflag) 1721 handle_stdin(); 1722 else 1723 handle_stdout(); 1724 return; 1725 } 1726 1727retry: 1728 if (stat(path, &sb) != 0) { 1729 /* lets try <path>.gz if we're decompressing */ 1730 if (dflag && s == NULL && errno == ENOENT) { 1731 len = strlen(path); 1732 slen = suffixes[0].ziplen; 1733 s = malloc(len + slen + 1); 1734 if (s == NULL) 1735 maybe_err("malloc"); 1736 memcpy(s, path, len); 1737 memcpy(s + len, suffixes[0].zipped, slen + 1); 1738 path = s; 1739 goto retry; 1740 } 1741 maybe_warn("can't stat: %s", opath); 1742 goto out; 1743 } 1744 1745 if (S_ISDIR(sb.st_mode)) { 1746#ifndef SMALL 1747 if (rflag) 1748 handle_dir(path); 1749 else 1750#endif 1751 maybe_warnx("%s is a directory", path); 1752 goto out; 1753 } 1754 1755 if (S_ISREG(sb.st_mode)) 1756 handle_file(path, &sb); 1757 else 1758 maybe_warnx("%s is not a regular file", path); 1759 1760out: 1761 if (s) 1762 free(s); 1763} 1764 1765/* compress/decompress a file */ 1766static void 1767handle_file(char *file, struct stat *sbp) 1768{ 1769 off_t usize, gsize; 1770 char outfile[PATH_MAX]; 1771 1772 infile = file; 1773 if (dflag) { 1774 usize = file_uncompress(file, outfile, sizeof(outfile)); 1775#ifndef SMALL 1776 if (vflag && tflag) 1777 print_test(file, usize != -1); 1778#endif 1779 if (usize == -1) 1780 return; 1781 gsize = sbp->st_size; 1782 } else { 1783 gsize = file_compress(file, outfile, sizeof(outfile)); 1784 if (gsize == -1) 1785 return; 1786 usize = sbp->st_size; 1787 } 1788 1789 1790#ifndef SMALL 1791 if (vflag && !tflag) 1792 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1793#endif 1794} 1795 1796#ifndef SMALL 1797/* this is used with -r to recursively descend directories */ 1798static void 1799handle_dir(char *dir) 1800{ 1801 char *path_argv[2]; 1802 FTS *fts; 1803 FTSENT *entry; 1804 1805 path_argv[0] = dir; 1806 path_argv[1] = 0; 1807 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 1808 if (fts == NULL) { 1809 warn("couldn't fts_open %s", dir); 1810 return; 1811 } 1812 1813 while ((entry = fts_read(fts))) { 1814 switch(entry->fts_info) { 1815 case FTS_D: 1816 case FTS_DP: 1817 continue; 1818 1819 case FTS_DNR: 1820 case FTS_ERR: 1821 case FTS_NS: 1822 maybe_warn("%s", entry->fts_path); 1823 continue; 1824 case FTS_F: 1825 handle_file(entry->fts_path, entry->fts_statp); 1826 } 1827 } 1828 (void)fts_close(fts); 1829} 1830#endif 1831 1832/* print a ratio - size reduction as a fraction of uncompressed size */ 1833static void 1834print_ratio(off_t in, off_t out, FILE *where) 1835{ 1836 int percent10; /* 10 * percent */ 1837 off_t diff; 1838 char buff[8]; 1839 int len; 1840 1841 diff = in - out/2; 1842 if (diff <= 0) 1843 /* 1844 * Output is more than double size of input! print -99.9% 1845 * Quite possibly we've failed to get the original size. 1846 */ 1847 percent10 = -999; 1848 else { 1849 /* 1850 * We only need 12 bits of result from the final division, 1851 * so reduce the values until a 32bit division will suffice. 1852 */ 1853 while (in > 0x100000) { 1854 diff >>= 1; 1855 in >>= 1; 1856 } 1857 if (in != 0) 1858 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 1859 else 1860 percent10 = 0; 1861 } 1862 1863 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 1864 /* Move the '.' to before the last digit */ 1865 buff[len - 1] = buff[len - 2]; 1866 buff[len - 2] = '.'; 1867 fprintf(where, "%5s%%", buff); 1868} 1869 1870#ifndef SMALL 1871/* print compression statistics, and the new name (if there is one!) */ 1872static void 1873print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 1874{ 1875 if (file) 1876 fprintf(stderr, "%s:%s ", file, 1877 strlen(file) < 7 ? "\t\t" : "\t"); 1878 print_ratio(usize, gsize, stderr); 1879 if (nfile) 1880 fprintf(stderr, " -- replaced with %s", nfile); 1881 fprintf(stderr, "\n"); 1882 fflush(stderr); 1883} 1884 1885/* print test results */ 1886static void 1887print_test(const char *file, int ok) 1888{ 1889 1890 if (exit_value == 0 && ok == 0) 1891 exit_value = 1; 1892 fprintf(stderr, "%s:%s %s\n", file, 1893 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1894 fflush(stderr); 1895} 1896#endif 1897 1898/* print a file's info ala --list */ 1899/* eg: 1900 compressed uncompressed ratio uncompressed_name 1901 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1902*/ 1903static void 1904print_list(int fd, off_t out, const char *outfile, time_t ts) 1905{ 1906 static int first = 1; 1907#ifndef SMALL 1908 static off_t in_tot, out_tot; 1909 uint32_t crc = 0; 1910#endif 1911 off_t in = 0, rv; 1912 1913 if (first) { 1914#ifndef SMALL 1915 if (vflag) 1916 printf("method crc date time "); 1917#endif 1918 if (qflag == 0) 1919 printf(" compressed uncompressed " 1920 "ratio uncompressed_name\n"); 1921 } 1922 first = 0; 1923 1924 /* print totals? */ 1925#ifndef SMALL 1926 if (fd == -1) { 1927 in = in_tot; 1928 out = out_tot; 1929 } else 1930#endif 1931 { 1932 /* read the last 4 bytes - this is the uncompressed size */ 1933 rv = lseek(fd, (off_t)(-8), SEEK_END); 1934 if (rv != -1) { 1935 unsigned char buf[8]; 1936 uint32_t usize; 1937 1938 rv = read(fd, (char *)buf, sizeof(buf)); 1939 if (rv == -1) 1940 maybe_warn("read of uncompressed size"); 1941 else if (rv != sizeof(buf)) 1942 maybe_warnx("read of uncompressed size"); 1943 1944 else { 1945 usize = buf[4] | buf[5] << 8 | 1946 buf[6] << 16 | buf[7] << 24; 1947 in = (off_t)usize; 1948#ifndef SMALL 1949 crc = buf[0] | buf[1] << 8 | 1950 buf[2] << 16 | buf[3] << 24; 1951#endif 1952 } 1953 } 1954 } 1955 1956#ifndef SMALL 1957 if (vflag && fd == -1) 1958 printf(" "); 1959 else if (vflag) { 1960 char *date = ctime(&ts); 1961 1962 /* skip the day, 1/100th second, and year */ 1963 date += 4; 1964 date[12] = 0; 1965 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 1966 } 1967 in_tot += in; 1968 out_tot += out; 1969#endif 1970 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 1971 print_ratio(in, out, stdout); 1972 printf(" %s\n", outfile); 1973} 1974 1975/* display the usage of NetBSD gzip */ 1976static void 1977usage(void) 1978{ 1979 1980 fprintf(stderr, "%s\n", gzip_version); 1981 fprintf(stderr, 1982#ifdef SMALL 1983 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n", 1984#else 1985 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n" 1986 " -1 --fast fastest (worst) compression\n" 1987 " -2 .. -8 set compression level\n" 1988 " -9 --best best (slowest) compression\n" 1989 " -c --stdout write to stdout, keep original files\n" 1990 " --to-stdout\n" 1991 " -d --decompress uncompress files\n" 1992 " --uncompress\n" 1993 " -f --force force overwriting & compress links\n" 1994 " -h --help display this help\n" 1995 " -k --keep don't delete input files during operation\n" 1996 " -l --list list compressed file contents\n" 1997 " -N --name save or restore original file name and time stamp\n" 1998 " -n --no-name don't save original file name or time stamp\n" 1999 " -q --quiet output no warnings\n" 2000 " -r --recursive recursively compress files in directories\n" 2001 " -S .suf use suffix .suf instead of .gz\n" 2002 " --suffix .suf\n" 2003 " -t --test test compressed file\n" 2004 " -V --version display program version\n" 2005 " -v --verbose print extra statistics\n", 2006#endif 2007 getprogname()); 2008 exit(0); 2009} 2010 2011#ifndef SMALL 2012/* display the license information of FreeBSD gzip */ 2013static void 2014display_license(void) 2015{ 2016 2017 fprintf(stderr, "%s (based on NetBSD gzip 20060927)\n", gzip_version); 2018 fprintf(stderr, "%s\n", gzip_copyright); 2019 exit(0); 2020} 2021#endif 2022 2023/* display the version of NetBSD gzip */ 2024static void 2025display_version(void) 2026{ 2027 2028 fprintf(stderr, "%s\n", gzip_version); 2029 exit(0); 2030} 2031 2032#ifndef NO_BZIP2_SUPPORT 2033#include "unbzip2.c" 2034#endif 2035#ifndef NO_COMPRESS_SUPPORT 2036#include "zuncompress.c" 2037#endif 2038 2039static ssize_t 2040read_retry(int fd, void *buf, size_t sz) 2041{ 2042 char *cp = buf; 2043 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2044 2045 while (left > 0) { 2046 ssize_t ret; 2047 2048 ret = read(fd, cp, left); 2049 if (ret == -1) { 2050 return ret; 2051 } else if (ret == 0) { 2052 break; /* EOF */ 2053 } 2054 cp += ret; 2055 left -= ret; 2056 } 2057 2058 return sz - left; 2059} 2060 2061