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