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