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