gzip.c revision 207247
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 207247 2010-04-26 20:05:48Z 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 * - 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#ifndef PRIdOFF 69#define PRIdOFF PRId64 70#endif 71 72/* what type of file are we dealing with */ 73enum filetype { 74 FT_GZIP, 75#ifndef NO_BZIP2_SUPPORT 76 FT_BZIP2, 77#endif 78#ifndef NO_COMPRESS_SUPPORT 79 FT_Z, 80#endif 81#ifndef NO_PACK_SUPPORT 82 FT_PACK, 83#endif 84 FT_LAST, 85 FT_UNKNOWN 86}; 87 88#ifndef NO_BZIP2_SUPPORT 89#include <bzlib.h> 90 91#define BZ2_SUFFIX ".bz2" 92#define BZIP2_MAGIC "\102\132\150" 93#endif 94 95#ifndef NO_COMPRESS_SUPPORT 96#define Z_SUFFIX ".Z" 97#define Z_MAGIC "\037\235" 98#endif 99 100#ifndef NO_PACK_SUPPORT 101#define PACK_MAGIC "\037\036" 102#endif 103 104#define GZ_SUFFIX ".gz" 105 106#define BUFLEN (64 * 1024) 107 108#define GZIP_MAGIC0 0x1F 109#define GZIP_MAGIC1 0x8B 110#define GZIP_OMAGIC1 0x9E 111 112#define GZIP_TIMESTAMP (off_t)4 113#define GZIP_ORIGNAME (off_t)10 114 115#define HEAD_CRC 0x02 116#define EXTRA_FIELD 0x04 117#define ORIG_NAME 0x08 118#define COMMENT 0x10 119 120#define OS_CODE 3 /* Unix */ 121 122typedef struct { 123 const char *zipped; 124 int ziplen; 125 const char *normal; /* for unzip - must not be longer than zipped */ 126} suffixes_t; 127static suffixes_t suffixes[] = { 128#define SUFFIX(Z, N) {Z, sizeof Z - 1, N} 129 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ 130#ifndef SMALL 131 SUFFIX(GZ_SUFFIX, ""), 132 SUFFIX(".z", ""), 133 SUFFIX("-gz", ""), 134 SUFFIX("-z", ""), 135 SUFFIX("_z", ""), 136 SUFFIX(".taz", ".tar"), 137 SUFFIX(".tgz", ".tar"), 138#ifndef NO_BZIP2_SUPPORT 139 SUFFIX(BZ2_SUFFIX, ""), 140 SUFFIX(".tbz", ".tar"), 141 SUFFIX(".tbz2", ".tar"), 142#endif 143#ifndef NO_COMPRESS_SUPPORT 144 SUFFIX(Z_SUFFIX, ""), 145#endif 146 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ 147#endif /* SMALL */ 148#undef SUFFIX 149}; 150#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) 151#define SUFFIX_MAXLEN 30 152 153static const char gzip_version[] = "FreeBSD gzip 20100407"; 154 155#ifndef SMALL 156static const char gzip_copyright[] = \ 157" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" 158" All rights reserved.\n" 159"\n" 160" Redistribution and use in source and binary forms, with or without\n" 161" modification, are permitted provided that the following conditions\n" 162" are met:\n" 163" 1. Redistributions of source code must retain the above copyright\n" 164" notice, this list of conditions and the following disclaimer.\n" 165" 2. Redistributions in binary form must reproduce the above copyright\n" 166" notice, this list of conditions and the following disclaimer in the\n" 167" documentation and/or other materials provided with the distribution.\n" 168"\n" 169" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 170" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 171" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 172" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" 173" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" 174" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" 175" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" 176" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" 177" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" 178" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" 179" SUCH DAMAGE."; 180#endif 181 182static int cflag; /* stdout mode */ 183static int dflag; /* decompress mode */ 184static int lflag; /* list mode */ 185static int numflag = 6; /* gzip -1..-9 value */ 186 187#ifndef SMALL 188static int fflag; /* force mode */ 189static int kflag; /* don't delete input files */ 190static int nflag; /* don't save name/timestamp */ 191static int Nflag; /* don't restore name/timestamp */ 192static int qflag; /* quiet mode */ 193static int rflag; /* recursive mode */ 194static int tflag; /* test */ 195static int vflag; /* verbose mode */ 196static const char *remove_file = NULL; /* file to be removed upon SIGINT */ 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); 234static void sigint_handler(int); 235#endif 236static const suffixes_t *check_suffix(char *, int); 237static ssize_t read_retry(int, void *, size_t); 238 239#ifdef SMALL 240#define unlink_input(f, sb) unlink(f) 241#else 242static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); 243static void prepend_gzip(char *, int *, char ***); 244static void handle_dir(char *); 245static void print_verbage(const char *, const char *, off_t, off_t); 246static void print_test(const char *, int); 247static void copymodes(int fd, const struct stat *, const char *file); 248static int check_outfile(const char *outfile); 249#endif 250 251#ifndef NO_BZIP2_SUPPORT 252static off_t unbzip2(int, int, char *, size_t, off_t *); 253#endif 254 255#ifndef NO_COMPRESS_SUPPORT 256static FILE *zdopen(int); 257static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); 258#endif 259 260#ifndef NO_PACK_SUPPORT 261static off_t unpack(int, int, char *, size_t, off_t *); 262#endif 263 264int main(int, char **p); 265 266#ifdef SMALL 267#define getopt_long(a,b,c,d,e) getopt(a,b,c) 268#else 269static const struct option longopts[] = { 270 { "stdout", no_argument, 0, 'c' }, 271 { "to-stdout", no_argument, 0, 'c' }, 272 { "decompress", no_argument, 0, 'd' }, 273 { "uncompress", no_argument, 0, 'd' }, 274 { "force", no_argument, 0, 'f' }, 275 { "help", no_argument, 0, 'h' }, 276 { "keep", no_argument, 0, 'k' }, 277 { "list", no_argument, 0, 'l' }, 278 { "no-name", no_argument, 0, 'n' }, 279 { "name", no_argument, 0, 'N' }, 280 { "quiet", no_argument, 0, 'q' }, 281 { "recursive", no_argument, 0, 'r' }, 282 { "suffix", required_argument, 0, 'S' }, 283 { "test", no_argument, 0, 't' }, 284 { "verbose", no_argument, 0, 'v' }, 285 { "version", no_argument, 0, 'V' }, 286 { "fast", no_argument, 0, '1' }, 287 { "best", no_argument, 0, '9' }, 288 { "ascii", no_argument, 0, 'a' }, 289 { "license", no_argument, 0, 'L' }, 290 { NULL, no_argument, 0, 0 }, 291}; 292#endif 293 294int 295main(int argc, char **argv) 296{ 297 const char *progname = getprogname(); 298#ifndef SMALL 299 char *gzip; 300 int len; 301#endif 302 int ch; 303 304#ifndef SMALL 305 if ((gzip = getenv("GZIP")) != NULL) 306 prepend_gzip(gzip, &argc, &argv); 307 signal(SIGINT, sigint_handler); 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 1175static void 1176sigint_handler(int signo __unused) 1177{ 1178 1179 if (remove_file != NULL) 1180 unlink(remove_file); 1181 exit(2); 1182} 1183#endif 1184 1185static const suffixes_t * 1186check_suffix(char *file, int xlate) 1187{ 1188 const suffixes_t *s; 1189 int len = strlen(file); 1190 char *sp; 1191 1192 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { 1193 /* if it doesn't fit in "a.suf", don't bother */ 1194 if (s->ziplen >= len) 1195 continue; 1196 sp = file + len - s->ziplen; 1197 if (strcmp(s->zipped, sp) != 0) 1198 continue; 1199 if (xlate) 1200 strcpy(sp, s->normal); 1201 return s; 1202 } 1203 return NULL; 1204} 1205 1206/* 1207 * compress the given file: create a corresponding .gz file and remove the 1208 * original. 1209 */ 1210static off_t 1211file_compress(char *file, char *outfile, size_t outsize) 1212{ 1213 int in; 1214 int out; 1215 off_t size, insize; 1216#ifndef SMALL 1217 struct stat isb, osb; 1218 const suffixes_t *suff; 1219#endif 1220 1221 in = open(file, O_RDONLY); 1222 if (in == -1) { 1223 maybe_warn("can't open %s", file); 1224 return -1; 1225 } 1226 1227 if (cflag == 0) { 1228#ifndef SMALL 1229 if (fstat(in, &isb) == 0) { 1230 if (isb.st_nlink > 1 && fflag == 0) { 1231 maybe_warnx("%s has %d other link%s -- " 1232 "skipping", file, isb.st_nlink - 1, 1233 isb.st_nlink == 1 ? "" : "s"); 1234 close(in); 1235 return -1; 1236 } 1237 } 1238 1239 if (fflag == 0 && (suff = check_suffix(file, 0)) 1240 && suff->zipped[0] != 0) { 1241 maybe_warnx("%s already has %s suffix -- unchanged", 1242 file, suff->zipped); 1243 close(in); 1244 return -1; 1245 } 1246#endif 1247 1248 /* Add (usually) .gz to filename */ 1249 if ((size_t)snprintf(outfile, outsize, "%s%s", 1250 file, suffixes[0].zipped) >= outsize) 1251 memcpy(outfile + outsize - suffixes[0].ziplen - 1, 1252 suffixes[0].zipped, suffixes[0].ziplen + 1); 1253 1254#ifndef SMALL 1255 if (check_outfile(outfile) == 0) { 1256 close(in); 1257 return -1; 1258 } 1259#endif 1260 } 1261 1262 if (cflag == 0) { 1263 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); 1264 if (out == -1) { 1265 maybe_warn("could not create output: %s", outfile); 1266 fclose(stdin); 1267 return -1; 1268 } 1269#ifndef SMALL 1270 remove_file = outfile; 1271#endif 1272 } else 1273 out = STDOUT_FILENO; 1274 1275 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); 1276 1277 (void)close(in); 1278 1279 /* 1280 * If there was an error, insize will be -1. 1281 * If we compressed to stdout, just return the size. 1282 * Otherwise stat the file and check it is the correct size. 1283 * We only blow away the file if we can stat the output and it 1284 * has the expected size. 1285 */ 1286 if (cflag != 0) 1287 return insize == -1 ? -1 : size; 1288 1289#ifndef SMALL 1290 if (fstat(out, &osb) != 0) { 1291 maybe_warn("couldn't stat: %s", outfile); 1292 goto bad_outfile; 1293 } 1294 1295 if (osb.st_size != size) { 1296 maybe_warnx("output file: %s wrong size (%" PRIdOFF 1297 " != %" PRIdOFF "), deleting", 1298 outfile, osb.st_size, size); 1299 goto bad_outfile; 1300 } 1301 1302 copymodes(out, &isb, outfile); 1303 remove_file = NULL; 1304#endif 1305 if (close(out) == -1) 1306 maybe_warn("couldn't close output"); 1307 1308 /* output is good, ok to delete input */ 1309 unlink_input(file, &isb); 1310 return size; 1311 1312#ifndef SMALL 1313 bad_outfile: 1314 if (close(out) == -1) 1315 maybe_warn("couldn't close output"); 1316 1317 maybe_warnx("leaving original %s", file); 1318 unlink(outfile); 1319 return size; 1320#endif 1321} 1322 1323/* uncompress the given file and remove the original */ 1324static off_t 1325file_uncompress(char *file, char *outfile, size_t outsize) 1326{ 1327 struct stat isb, osb; 1328 off_t size; 1329 ssize_t rbytes; 1330 unsigned char header1[4]; 1331 enum filetype method; 1332 int fd, ofd, zfd = -1; 1333#ifndef SMALL 1334 ssize_t rv; 1335 time_t timestamp = 0; 1336 unsigned char name[PATH_MAX + 1]; 1337#endif 1338 1339 /* gather the old name info */ 1340 1341 fd = open(file, O_RDONLY); 1342 if (fd < 0) { 1343 maybe_warn("can't open %s", file); 1344 goto lose; 1345 } 1346 1347 strlcpy(outfile, file, outsize); 1348 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1349 maybe_warnx("%s: unknown suffix -- ignored", file); 1350 goto lose; 1351 } 1352 1353 rbytes = read(fd, header1, sizeof header1); 1354 if (rbytes != sizeof header1) { 1355 /* we don't want to fail here. */ 1356#ifndef SMALL 1357 if (fflag) 1358 goto lose; 1359#endif 1360 if (rbytes == -1) 1361 maybe_warn("can't read %s", file); 1362 else 1363 goto unexpected_EOF; 1364 goto lose; 1365 } 1366 1367 method = file_gettype(header1); 1368 1369#ifndef SMALL 1370 if (fflag == 0 && method == FT_UNKNOWN) { 1371 maybe_warnx("%s: not in gzip format", file); 1372 goto lose; 1373 } 1374 1375#endif 1376 1377#ifndef SMALL 1378 if (method == FT_GZIP && Nflag) { 1379 unsigned char ts[4]; /* timestamp */ 1380 1381 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1382 if (rv >= 0 && rv < (ssize_t)(sizeof ts)) 1383 goto unexpected_EOF; 1384 if (rv == -1) { 1385 if (!fflag) 1386 maybe_warn("can't read %s", file); 1387 goto lose; 1388 } 1389 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; 1390 1391 if (header1[3] & ORIG_NAME) { 1392 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME); 1393 if (rbytes < 0) { 1394 maybe_warn("can't read %s", file); 1395 goto lose; 1396 } 1397 if (name[0] != 0) { 1398 /* preserve original directory name */ 1399 char *dp = strrchr(file, '/'); 1400 if (dp == NULL) 1401 dp = file; 1402 else 1403 dp++; 1404 snprintf(outfile, outsize, "%.*s%.*s", 1405 (int) (dp - file), 1406 file, (int) rbytes, name); 1407 } 1408 } 1409 } 1410#endif 1411 lseek(fd, 0, SEEK_SET); 1412 1413 if (cflag == 0 || lflag) { 1414 if (fstat(fd, &isb) != 0) 1415 goto lose; 1416#ifndef SMALL 1417 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1418 maybe_warnx("%s has %d other links -- skipping", 1419 file, isb.st_nlink - 1); 1420 goto lose; 1421 } 1422 if (nflag == 0 && timestamp) 1423 isb.st_mtime = timestamp; 1424 if (check_outfile(outfile) == 0) 1425 goto lose; 1426#endif 1427 } 1428 1429 if (cflag == 0 && lflag == 0) { 1430 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1431 if (zfd == STDOUT_FILENO) { 1432 /* We won't close STDOUT_FILENO later... */ 1433 zfd = dup(zfd); 1434 close(STDOUT_FILENO); 1435 } 1436 if (zfd == -1) { 1437 maybe_warn("can't open %s", outfile); 1438 goto lose; 1439 } 1440#ifndef SMALL 1441 remove_file = outfile; 1442#endif 1443 } else 1444 zfd = STDOUT_FILENO; 1445 1446#ifndef NO_BZIP2_SUPPORT 1447 if (method == FT_BZIP2) { 1448 1449 /* XXX */ 1450 if (lflag) { 1451 maybe_warnx("no -l with bzip2 files"); 1452 goto lose; 1453 } 1454 1455 size = unbzip2(fd, zfd, NULL, 0, NULL); 1456 } else 1457#endif 1458 1459#ifndef NO_COMPRESS_SUPPORT 1460 if (method == FT_Z) { 1461 FILE *in, *out; 1462 1463 /* XXX */ 1464 if (lflag) { 1465 maybe_warnx("no -l with Lempel-Ziv files"); 1466 goto lose; 1467 } 1468 1469 if ((in = zdopen(fd)) == NULL) { 1470 maybe_warn("zdopen for read: %s", file); 1471 goto lose; 1472 } 1473 1474 out = fdopen(dup(zfd), "w"); 1475 if (out == NULL) { 1476 maybe_warn("fdopen for write: %s", outfile); 1477 fclose(in); 1478 goto lose; 1479 } 1480 1481 size = zuncompress(in, out, NULL, 0, NULL); 1482 /* need to fclose() if ferror() is true... */ 1483 if (ferror(in) | fclose(in)) { 1484 maybe_warn("failed infile fclose"); 1485 unlink(outfile); 1486 (void)fclose(out); 1487 } 1488 if (fclose(out) != 0) { 1489 maybe_warn("failed outfile fclose"); 1490 unlink(outfile); 1491 goto lose; 1492 } 1493 } else 1494#endif 1495 1496#ifndef NO_PACK_SUPPORT 1497 if (method == FT_PACK) { 1498 if (lflag) { 1499 maybe_warnx("no -l with packed files"); 1500 goto lose; 1501 } 1502 1503 size = unpack(fd, zfd, NULL, 0, NULL); 1504 } else 1505#endif 1506 1507#ifndef SMALL 1508 if (method == FT_UNKNOWN) { 1509 if (lflag) { 1510 maybe_warnx("no -l for unknown filetypes"); 1511 goto lose; 1512 } 1513 size = cat_fd(NULL, 0, NULL, fd); 1514 } else 1515#endif 1516 { 1517 if (lflag) { 1518 print_list(fd, isb.st_size, outfile, isb.st_mtime); 1519 close(fd); 1520 return -1; /* XXX */ 1521 } 1522 1523 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1524 } 1525 1526 if (close(fd) != 0) 1527 maybe_warn("couldn't close input"); 1528 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1529 maybe_warn("couldn't close output"); 1530 1531 if (size == -1) { 1532 if (cflag == 0) 1533 unlink(outfile); 1534 maybe_warnx("%s: uncompress failed", file); 1535 return -1; 1536 } 1537 1538 /* if testing, or we uncompressed to stdout, this is all we need */ 1539#ifndef SMALL 1540 if (tflag) 1541 return size; 1542#endif 1543 /* if we are uncompressing to stdin, don't remove the file. */ 1544 if (cflag) 1545 return size; 1546 1547 /* 1548 * if we create a file... 1549 */ 1550 /* 1551 * if we can't stat the file don't remove the file. 1552 */ 1553 1554 ofd = open(outfile, O_RDWR, 0); 1555 if (ofd == -1) { 1556 maybe_warn("couldn't open (leaving original): %s", 1557 outfile); 1558 return -1; 1559 } 1560 if (fstat(ofd, &osb) != 0) { 1561 maybe_warn("couldn't stat (leaving original): %s", 1562 outfile); 1563 close(ofd); 1564 return -1; 1565 } 1566 if (osb.st_size != size) { 1567 maybe_warnx("stat gave different size: %" PRIdOFF 1568 " != %" PRIdOFF " (leaving original)", 1569 size, osb.st_size); 1570 close(ofd); 1571 unlink(outfile); 1572 return -1; 1573 } 1574#ifndef SMALL 1575 copymodes(ofd, &isb, outfile); 1576 remove_file = NULL; 1577#endif 1578 close(ofd); 1579 unlink_input(file, &isb); 1580 return size; 1581 1582 unexpected_EOF: 1583 maybe_warnx("%s: unexpected end of file", file); 1584 lose: 1585 if (fd != -1) 1586 close(fd); 1587 if (zfd != -1 && zfd != STDOUT_FILENO) 1588 close(fd); 1589 return -1; 1590} 1591 1592#ifndef SMALL 1593static off_t 1594cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1595{ 1596 char buf[BUFLEN]; 1597 off_t in_tot; 1598 ssize_t w; 1599 1600 in_tot = count; 1601 w = write(STDOUT_FILENO, prepend, count); 1602 if (w == -1 || (size_t)w != count) { 1603 maybe_warn("write to stdout"); 1604 return -1; 1605 } 1606 for (;;) { 1607 ssize_t rv; 1608 1609 rv = read(fd, buf, sizeof buf); 1610 if (rv == 0) 1611 break; 1612 if (rv < 0) { 1613 maybe_warn("read from fd %d", fd); 1614 break; 1615 } 1616 1617 if (write(STDOUT_FILENO, buf, rv) != rv) { 1618 maybe_warn("write to stdout"); 1619 break; 1620 } 1621 in_tot += rv; 1622 } 1623 1624 if (gsizep) 1625 *gsizep = in_tot; 1626 return (in_tot); 1627} 1628#endif 1629 1630static void 1631handle_stdin(void) 1632{ 1633 unsigned char header1[4]; 1634 off_t usize, gsize; 1635 enum filetype method; 1636 ssize_t bytes_read; 1637#ifndef NO_COMPRESS_SUPPORT 1638 FILE *in; 1639#endif 1640 1641#ifndef SMALL 1642 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1643 maybe_warnx("standard input is a terminal -- ignoring"); 1644 return; 1645 } 1646#endif 1647 1648 if (lflag) { 1649 struct stat isb; 1650 1651 /* XXX could read the whole file, etc. */ 1652 if (fstat(STDIN_FILENO, &isb) < 0) { 1653 maybe_warn("fstat"); 1654 return; 1655 } 1656 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1657 return; 1658 } 1659 1660 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1); 1661 if (bytes_read == -1) { 1662 maybe_warn("can't read stdin"); 1663 return; 1664 } else if (bytes_read != sizeof(header1)) { 1665 maybe_warnx("(stdin): unexpected end of file"); 1666 return; 1667 } 1668 1669 method = file_gettype(header1); 1670 switch (method) { 1671 default: 1672#ifndef SMALL 1673 if (fflag == 0) { 1674 maybe_warnx("unknown compression format"); 1675 return; 1676 } 1677 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO); 1678 break; 1679#endif 1680 case FT_GZIP: 1681 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1682 (char *)header1, sizeof header1, &gsize, "(stdin)"); 1683 break; 1684#ifndef NO_BZIP2_SUPPORT 1685 case FT_BZIP2: 1686 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1687 (char *)header1, sizeof header1, &gsize); 1688 break; 1689#endif 1690#ifndef NO_COMPRESS_SUPPORT 1691 case FT_Z: 1692 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1693 maybe_warnx("zopen of stdin"); 1694 return; 1695 } 1696 1697 usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize); 1698 fclose(in); 1699 break; 1700#endif 1701#ifndef NO_PACK_SUPPORT 1702 case FT_PACK: 1703 usize = unpack(STDIN_FILENO, STDOUT_FILENO, 1704 (char *)header1, sizeof header1, &gsize); 1705 break; 1706#endif 1707 } 1708 1709#ifndef SMALL 1710 if (vflag && !tflag && usize != -1 && gsize != -1) 1711 print_verbage(NULL, NULL, usize, gsize); 1712 if (vflag && tflag) 1713 print_test("(stdin)", usize != -1); 1714#endif 1715 1716} 1717 1718static void 1719handle_stdout(void) 1720{ 1721 off_t gsize, usize; 1722 struct stat sb; 1723 time_t systime; 1724 uint32_t mtime; 1725 int ret; 1726 1727#ifndef SMALL 1728 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1729 maybe_warnx("standard output is a terminal -- ignoring"); 1730 return; 1731 } 1732#endif 1733 /* If stdin is a file use it's mtime, otherwise use current time */ 1734 ret = fstat(STDIN_FILENO, &sb); 1735 1736#ifndef SMALL 1737 if (ret < 0) { 1738 maybe_warn("Can't stat stdin"); 1739 return; 1740 } 1741#endif 1742 1743 if (S_ISREG(sb.st_mode)) 1744 mtime = (uint32_t)sb.st_mtime; 1745 else { 1746 systime = time(NULL); 1747#ifndef SMALL 1748 if (systime == -1) { 1749 maybe_warn("time"); 1750 return; 1751 } 1752#endif 1753 mtime = (uint32_t)systime; 1754 } 1755 1756 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1757#ifndef SMALL 1758 if (vflag && !tflag && usize != -1 && gsize != -1) 1759 print_verbage(NULL, NULL, usize, gsize); 1760#endif 1761} 1762 1763/* do what is asked for, for the path name */ 1764static void 1765handle_pathname(char *path) 1766{ 1767 char *opath = path, *s = NULL; 1768 ssize_t len; 1769 int slen; 1770 struct stat sb; 1771 1772 /* check for stdout/stdin */ 1773 if (path[0] == '-' && path[1] == '\0') { 1774 if (dflag) 1775 handle_stdin(); 1776 else 1777 handle_stdout(); 1778 return; 1779 } 1780 1781retry: 1782 if (stat(path, &sb) != 0) { 1783 /* lets try <path>.gz if we're decompressing */ 1784 if (dflag && s == NULL && errno == ENOENT) { 1785 len = strlen(path); 1786 slen = suffixes[0].ziplen; 1787 s = malloc(len + slen + 1); 1788 if (s == NULL) 1789 maybe_err("malloc"); 1790 memcpy(s, path, len); 1791 memcpy(s + len, suffixes[0].zipped, slen + 1); 1792 path = s; 1793 goto retry; 1794 } 1795 maybe_warn("can't stat: %s", opath); 1796 goto out; 1797 } 1798 1799 if (S_ISDIR(sb.st_mode)) { 1800#ifndef SMALL 1801 if (rflag) 1802 handle_dir(path); 1803 else 1804#endif 1805 maybe_warnx("%s is a directory", path); 1806 goto out; 1807 } 1808 1809 if (S_ISREG(sb.st_mode)) 1810 handle_file(path, &sb); 1811 else 1812 maybe_warnx("%s is not a regular file", path); 1813 1814out: 1815 if (s) 1816 free(s); 1817} 1818 1819/* compress/decompress a file */ 1820static void 1821handle_file(char *file, struct stat *sbp) 1822{ 1823 off_t usize, gsize; 1824 char outfile[PATH_MAX]; 1825 1826 infile = file; 1827 if (dflag) { 1828 usize = file_uncompress(file, outfile, sizeof(outfile)); 1829#ifndef SMALL 1830 if (vflag && tflag) 1831 print_test(file, usize != -1); 1832#endif 1833 if (usize == -1) 1834 return; 1835 gsize = sbp->st_size; 1836 } else { 1837 gsize = file_compress(file, outfile, sizeof(outfile)); 1838 if (gsize == -1) 1839 return; 1840 usize = sbp->st_size; 1841 } 1842 1843 1844#ifndef SMALL 1845 if (vflag && !tflag) 1846 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1847#endif 1848} 1849 1850#ifndef SMALL 1851/* this is used with -r to recursively descend directories */ 1852static void 1853handle_dir(char *dir) 1854{ 1855 char *path_argv[2]; 1856 FTS *fts; 1857 FTSENT *entry; 1858 1859 path_argv[0] = dir; 1860 path_argv[1] = 0; 1861 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 1862 if (fts == NULL) { 1863 warn("couldn't fts_open %s", dir); 1864 return; 1865 } 1866 1867 while ((entry = fts_read(fts))) { 1868 switch(entry->fts_info) { 1869 case FTS_D: 1870 case FTS_DP: 1871 continue; 1872 1873 case FTS_DNR: 1874 case FTS_ERR: 1875 case FTS_NS: 1876 maybe_warn("%s", entry->fts_path); 1877 continue; 1878 case FTS_F: 1879 handle_file(entry->fts_path, entry->fts_statp); 1880 } 1881 } 1882 (void)fts_close(fts); 1883} 1884#endif 1885 1886/* print a ratio - size reduction as a fraction of uncompressed size */ 1887static void 1888print_ratio(off_t in, off_t out, FILE *where) 1889{ 1890 int percent10; /* 10 * percent */ 1891 off_t diff; 1892 char buff[8]; 1893 int len; 1894 1895 diff = in - out/2; 1896 if (diff <= 0) 1897 /* 1898 * Output is more than double size of input! print -99.9% 1899 * Quite possibly we've failed to get the original size. 1900 */ 1901 percent10 = -999; 1902 else { 1903 /* 1904 * We only need 12 bits of result from the final division, 1905 * so reduce the values until a 32bit division will suffice. 1906 */ 1907 while (in > 0x100000) { 1908 diff >>= 1; 1909 in >>= 1; 1910 } 1911 if (in != 0) 1912 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 1913 else 1914 percent10 = 0; 1915 } 1916 1917 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 1918 /* Move the '.' to before the last digit */ 1919 buff[len - 1] = buff[len - 2]; 1920 buff[len - 2] = '.'; 1921 fprintf(where, "%5s%%", buff); 1922} 1923 1924#ifndef SMALL 1925/* print compression statistics, and the new name (if there is one!) */ 1926static void 1927print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 1928{ 1929 if (file) 1930 fprintf(stderr, "%s:%s ", file, 1931 strlen(file) < 7 ? "\t\t" : "\t"); 1932 print_ratio(usize, gsize, stderr); 1933 if (nfile) 1934 fprintf(stderr, " -- replaced with %s", nfile); 1935 fprintf(stderr, "\n"); 1936 fflush(stderr); 1937} 1938 1939/* print test results */ 1940static void 1941print_test(const char *file, int ok) 1942{ 1943 1944 if (exit_value == 0 && ok == 0) 1945 exit_value = 1; 1946 fprintf(stderr, "%s:%s %s\n", file, 1947 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1948 fflush(stderr); 1949} 1950#endif 1951 1952/* print a file's info ala --list */ 1953/* eg: 1954 compressed uncompressed ratio uncompressed_name 1955 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1956*/ 1957static void 1958print_list(int fd, off_t out, const char *outfile, time_t ts) 1959{ 1960 static int first = 1; 1961#ifndef SMALL 1962 static off_t in_tot, out_tot; 1963 uint32_t crc = 0; 1964#endif 1965 off_t in = 0, rv; 1966 1967 if (first) { 1968#ifndef SMALL 1969 if (vflag) 1970 printf("method crc date time "); 1971#endif 1972 if (qflag == 0) 1973 printf(" compressed uncompressed " 1974 "ratio uncompressed_name\n"); 1975 } 1976 first = 0; 1977 1978 /* print totals? */ 1979#ifndef SMALL 1980 if (fd == -1) { 1981 in = in_tot; 1982 out = out_tot; 1983 } else 1984#endif 1985 { 1986 /* read the last 4 bytes - this is the uncompressed size */ 1987 rv = lseek(fd, (off_t)(-8), SEEK_END); 1988 if (rv != -1) { 1989 unsigned char buf[8]; 1990 uint32_t usize; 1991 1992 rv = read(fd, (char *)buf, sizeof(buf)); 1993 if (rv == -1) 1994 maybe_warn("read of uncompressed size"); 1995 else if (rv != sizeof(buf)) 1996 maybe_warnx("read of uncompressed size"); 1997 1998 else { 1999 usize = buf[4] | buf[5] << 8 | 2000 buf[6] << 16 | buf[7] << 24; 2001 in = (off_t)usize; 2002#ifndef SMALL 2003 crc = buf[0] | buf[1] << 8 | 2004 buf[2] << 16 | buf[3] << 24; 2005#endif 2006 } 2007 } 2008 } 2009 2010#ifndef SMALL 2011 if (vflag && fd == -1) 2012 printf(" "); 2013 else if (vflag) { 2014 char *date = ctime(&ts); 2015 2016 /* skip the day, 1/100th second, and year */ 2017 date += 4; 2018 date[12] = 0; 2019 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 2020 } 2021 in_tot += in; 2022 out_tot += out; 2023#else 2024 (void)&ts; /* XXX */ 2025#endif 2026 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 2027 print_ratio(in, out, stdout); 2028 printf(" %s\n", outfile); 2029} 2030 2031/* display the usage of NetBSD gzip */ 2032static void 2033usage(void) 2034{ 2035 2036 fprintf(stderr, "%s\n", gzip_version); 2037 fprintf(stderr, 2038#ifdef SMALL 2039 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n", 2040#else 2041 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n" 2042 " -1 --fast fastest (worst) compression\n" 2043 " -2 .. -8 set compression level\n" 2044 " -9 --best best (slowest) compression\n" 2045 " -c --stdout write to stdout, keep original files\n" 2046 " --to-stdout\n" 2047 " -d --decompress uncompress files\n" 2048 " --uncompress\n" 2049 " -f --force force overwriting & compress links\n" 2050 " -h --help display this help\n" 2051 " -k --keep don't delete input files during operation\n" 2052 " -l --list list compressed file contents\n" 2053 " -N --name save or restore original file name and time stamp\n" 2054 " -n --no-name don't save original file name or time stamp\n" 2055 " -q --quiet output no warnings\n" 2056 " -r --recursive recursively compress files in directories\n" 2057 " -S .suf use suffix .suf instead of .gz\n" 2058 " --suffix .suf\n" 2059 " -t --test test compressed file\n" 2060 " -V --version display program version\n" 2061 " -v --verbose print extra statistics\n", 2062#endif 2063 getprogname()); 2064 exit(0); 2065} 2066 2067#ifndef SMALL 2068/* display the license information of FreeBSD gzip */ 2069static void 2070display_license(void) 2071{ 2072 2073 fprintf(stderr, "%s (based on NetBSD gzip 20091011)\n", gzip_version); 2074 fprintf(stderr, "%s\n", gzip_copyright); 2075 exit(0); 2076} 2077#endif 2078 2079/* display the version of NetBSD gzip */ 2080static void 2081display_version(void) 2082{ 2083 2084 fprintf(stderr, "%s\n", gzip_version); 2085 exit(0); 2086} 2087 2088#ifndef NO_BZIP2_SUPPORT 2089#include "unbzip2.c" 2090#endif 2091#ifndef NO_COMPRESS_SUPPORT 2092#include "zuncompress.c" 2093#endif 2094#ifndef NO_PACK_SUPPORT 2095#include "unpack.c" 2096#endif 2097 2098static ssize_t 2099read_retry(int fd, void *buf, size_t sz) 2100{ 2101 char *cp = buf; 2102 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2103 2104 while (left > 0) { 2105 ssize_t ret; 2106 2107 ret = read(fd, cp, left); 2108 if (ret == -1) { 2109 return ret; 2110 } else if (ret == 0) { 2111 break; /* EOF */ 2112 } 2113 cp += ret; 2114 left -= ret; 2115 } 2116 2117 return sz - left; 2118} 2119