gzip.c revision 281626
1/* $NetBSD: gzip.c,v 1.108 2015/04/15 02:29:12 christos 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: head/usr.bin/gzip/gzip.c 281626 2015-04-16 22:30:57Z 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 goto stop; 814 } 815 maybe_warnx("input not gziped (MAGIC0)"); 816 goto stop_and_fail; 817 } 818 ADVANCE(); 819 state++; 820 out_sub_tot = 0; 821 crc = crc32(0L, Z_NULL, 0); 822 break; 823 824 case GZSTATE_MAGIC1: 825 if (*z.next_in != GZIP_MAGIC1 && 826 *z.next_in != GZIP_OMAGIC1) { 827 maybe_warnx("input not gziped (MAGIC1)"); 828 goto stop_and_fail; 829 } 830 ADVANCE(); 831 state++; 832 break; 833 834 case GZSTATE_METHOD: 835 if (*z.next_in != Z_DEFLATED) { 836 maybe_warnx("unknown compression method"); 837 goto stop_and_fail; 838 } 839 ADVANCE(); 840 state++; 841 break; 842 843 case GZSTATE_FLAGS: 844 flags = *z.next_in; 845 ADVANCE(); 846 skip_count = 6; 847 state++; 848 break; 849 850 case GZSTATE_SKIPPING: 851 if (skip_count > 0) { 852 skip_count--; 853 ADVANCE(); 854 } else 855 state++; 856 break; 857 858 case GZSTATE_EXTRA: 859 if ((flags & EXTRA_FIELD) == 0) { 860 state = GZSTATE_ORIGNAME; 861 break; 862 } 863 skip_count = *z.next_in; 864 ADVANCE(); 865 state++; 866 break; 867 868 case GZSTATE_EXTRA2: 869 skip_count |= ((*z.next_in) << 8); 870 ADVANCE(); 871 state++; 872 break; 873 874 case GZSTATE_EXTRA3: 875 if (skip_count > 0) { 876 skip_count--; 877 ADVANCE(); 878 } else 879 state++; 880 break; 881 882 case GZSTATE_ORIGNAME: 883 if ((flags & ORIG_NAME) == 0) { 884 state++; 885 break; 886 } 887 if (*z.next_in == 0) 888 state++; 889 ADVANCE(); 890 break; 891 892 case GZSTATE_COMMENT: 893 if ((flags & COMMENT) == 0) { 894 state++; 895 break; 896 } 897 if (*z.next_in == 0) 898 state++; 899 ADVANCE(); 900 break; 901 902 case GZSTATE_HEAD_CRC1: 903 if (flags & HEAD_CRC) 904 skip_count = 2; 905 else 906 skip_count = 0; 907 state++; 908 break; 909 910 case GZSTATE_HEAD_CRC2: 911 if (skip_count > 0) { 912 skip_count--; 913 ADVANCE(); 914 } else 915 state++; 916 break; 917 918 case GZSTATE_INIT: 919 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 920 maybe_warnx("failed to inflateInit"); 921 goto stop_and_fail; 922 } 923 state++; 924 break; 925 926 case GZSTATE_READ: 927 error = inflate(&z, Z_FINISH); 928 switch (error) { 929 /* Z_BUF_ERROR goes with Z_FINISH... */ 930 case Z_BUF_ERROR: 931 if (z.avail_out > 0 && !done_reading) 932 continue; 933 934 case Z_STREAM_END: 935 case Z_OK: 936 break; 937 938 case Z_NEED_DICT: 939 maybe_warnx("Z_NEED_DICT error"); 940 goto stop_and_fail; 941 case Z_DATA_ERROR: 942 maybe_warnx("data stream error"); 943 goto stop_and_fail; 944 case Z_STREAM_ERROR: 945 maybe_warnx("internal stream error"); 946 goto stop_and_fail; 947 case Z_MEM_ERROR: 948 maybe_warnx("memory allocation error"); 949 goto stop_and_fail; 950 951 default: 952 maybe_warn("unknown error from inflate(): %d", 953 error); 954 } 955 wr = BUFLEN - z.avail_out; 956 957 if (wr != 0) { 958 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); 959 if ( 960#ifndef SMALL 961 /* don't write anything with -t */ 962 tflag == 0 && 963#endif 964 write(out, outbufp, wr) != wr) { 965 maybe_warn("error writing to output"); 966 goto stop_and_fail; 967 } 968 969 out_tot += wr; 970 out_sub_tot += wr; 971 } 972 973 if (error == Z_STREAM_END) { 974 inflateEnd(&z); 975 state++; 976 } 977 978 z.next_out = (unsigned char *)outbufp; 979 z.avail_out = BUFLEN; 980 981 break; 982 case GZSTATE_CRC: 983 { 984 uLong origcrc; 985 986 if (z.avail_in < 4) { 987 if (!done_reading) { 988 needmore = 1; 989 continue; 990 } 991 maybe_warnx("truncated input"); 992 goto stop_and_fail; 993 } 994 origcrc = ((unsigned)z.next_in[0] & 0xff) | 995 ((unsigned)z.next_in[1] & 0xff) << 8 | 996 ((unsigned)z.next_in[2] & 0xff) << 16 | 997 ((unsigned)z.next_in[3] & 0xff) << 24; 998 if (origcrc != crc) { 999 maybe_warnx("invalid compressed" 1000 " data--crc error"); 1001 goto stop_and_fail; 1002 } 1003 } 1004 1005 z.avail_in -= 4; 1006 z.next_in += 4; 1007 1008 if (!z.avail_in && done_reading) { 1009 goto stop; 1010 } 1011 state++; 1012 break; 1013 case GZSTATE_LEN: 1014 { 1015 uLong origlen; 1016 1017 if (z.avail_in < 4) { 1018 if (!done_reading) { 1019 needmore = 1; 1020 continue; 1021 } 1022 maybe_warnx("truncated input"); 1023 goto stop_and_fail; 1024 } 1025 origlen = ((unsigned)z.next_in[0] & 0xff) | 1026 ((unsigned)z.next_in[1] & 0xff) << 8 | 1027 ((unsigned)z.next_in[2] & 0xff) << 16 | 1028 ((unsigned)z.next_in[3] & 0xff) << 24; 1029 1030 if (origlen != out_sub_tot) { 1031 maybe_warnx("invalid compressed" 1032 " data--length error"); 1033 goto stop_and_fail; 1034 } 1035 } 1036 1037 z.avail_in -= 4; 1038 z.next_in += 4; 1039 1040 if (error < 0) { 1041 maybe_warnx("decompression error"); 1042 goto stop_and_fail; 1043 } 1044 state = GZSTATE_MAGIC0; 1045 break; 1046 } 1047 continue; 1048stop_and_fail: 1049 out_tot = -1; 1050stop: 1051 break; 1052 } 1053 if (state > GZSTATE_INIT) 1054 inflateEnd(&z); 1055 1056 free(inbufp); 1057out1: 1058 free(outbufp); 1059out2: 1060 if (gsizep) 1061 *gsizep = in_tot; 1062 return (out_tot); 1063} 1064 1065#ifndef SMALL 1066/* 1067 * set the owner, mode, flags & utimes using the given file descriptor. 1068 * file is only used in possible warning messages. 1069 */ 1070static void 1071copymodes(int fd, const struct stat *sbp, const char *file) 1072{ 1073 struct timespec times[2]; 1074 struct stat sb; 1075 1076 /* 1077 * If we have no info on the input, give this file some 1078 * default values and return.. 1079 */ 1080 if (sbp == NULL) { 1081 mode_t mask = umask(022); 1082 1083 (void)fchmod(fd, DEFFILEMODE & ~mask); 1084 (void)umask(mask); 1085 return; 1086 } 1087 sb = *sbp; 1088 1089 /* if the chown fails, remove set-id bits as-per compress(1) */ 1090 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { 1091 if (errno != EPERM) 1092 maybe_warn("couldn't fchown: %s", file); 1093 sb.st_mode &= ~(S_ISUID|S_ISGID); 1094 } 1095 1096 /* we only allow set-id and the 9 normal permission bits */ 1097 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 1098 if (fchmod(fd, sb.st_mode) < 0) 1099 maybe_warn("couldn't fchmod: %s", file); 1100 1101 times[0] = sb.st_atim; 1102 times[1] = sb.st_mtim; 1103 if (futimens(fd, times) < 0) 1104 maybe_warn("couldn't futimens: %s", file); 1105 1106 /* only try flags if they exist already */ 1107 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) 1108 maybe_warn("couldn't fchflags: %s", file); 1109} 1110#endif 1111 1112/* what sort of file is this? */ 1113static enum filetype 1114file_gettype(u_char *buf) 1115{ 1116 1117 if (buf[0] == GZIP_MAGIC0 && 1118 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 1119 return FT_GZIP; 1120 else 1121#ifndef NO_BZIP2_SUPPORT 1122 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 1123 buf[3] >= '0' && buf[3] <= '9') 1124 return FT_BZIP2; 1125 else 1126#endif 1127#ifndef NO_COMPRESS_SUPPORT 1128 if (memcmp(buf, Z_MAGIC, 2) == 0) 1129 return FT_Z; 1130 else 1131#endif 1132#ifndef NO_PACK_SUPPORT 1133 if (memcmp(buf, PACK_MAGIC, 2) == 0) 1134 return FT_PACK; 1135 else 1136#endif 1137#ifndef NO_XZ_SUPPORT 1138 if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */ 1139 return FT_XZ; 1140 else 1141#endif 1142 return FT_UNKNOWN; 1143} 1144 1145#ifndef SMALL 1146/* check the outfile is OK. */ 1147static int 1148check_outfile(const char *outfile) 1149{ 1150 struct stat sb; 1151 int ok = 1; 1152 1153 if (lflag == 0 && stat(outfile, &sb) == 0) { 1154 if (fflag) 1155 unlink(outfile); 1156 else if (isatty(STDIN_FILENO)) { 1157 char ans[10] = { 'n', '\0' }; /* default */ 1158 1159 fprintf(stderr, "%s already exists -- do you wish to " 1160 "overwrite (y or n)? " , outfile); 1161 (void)fgets(ans, sizeof(ans) - 1, stdin); 1162 if (ans[0] != 'y' && ans[0] != 'Y') { 1163 fprintf(stderr, "\tnot overwriting\n"); 1164 ok = 0; 1165 } else 1166 unlink(outfile); 1167 } else { 1168 maybe_warnx("%s already exists -- skipping", outfile); 1169 ok = 0; 1170 } 1171 } 1172 return ok; 1173} 1174 1175static void 1176unlink_input(const char *file, const struct stat *sb) 1177{ 1178 struct stat nsb; 1179 1180 if (kflag) 1181 return; 1182 if (stat(file, &nsb) != 0) 1183 /* Must be gone already */ 1184 return; 1185 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) 1186 /* Definitely a different file */ 1187 return; 1188 unlink(file); 1189} 1190 1191static void 1192sigint_handler(int signo __unused) 1193{ 1194 1195 if (remove_file != NULL) 1196 unlink(remove_file); 1197 _exit(2); 1198} 1199#endif 1200 1201static const suffixes_t * 1202check_suffix(char *file, int xlate) 1203{ 1204 const suffixes_t *s; 1205 int len = strlen(file); 1206 char *sp; 1207 1208 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { 1209 /* if it doesn't fit in "a.suf", don't bother */ 1210 if (s->ziplen >= len) 1211 continue; 1212 sp = file + len - s->ziplen; 1213 if (strcmp(s->zipped, sp) != 0) 1214 continue; 1215 if (xlate) 1216 strcpy(sp, s->normal); 1217 return s; 1218 } 1219 return NULL; 1220} 1221 1222/* 1223 * compress the given file: create a corresponding .gz file and remove the 1224 * original. 1225 */ 1226static off_t 1227file_compress(char *file, char *outfile, size_t outsize) 1228{ 1229 int in; 1230 int out; 1231 off_t size, insize; 1232#ifndef SMALL 1233 struct stat isb, osb; 1234 const suffixes_t *suff; 1235#endif 1236 1237 in = open(file, O_RDONLY); 1238 if (in == -1) { 1239 maybe_warn("can't open %s", file); 1240 return (-1); 1241 } 1242 1243#ifndef SMALL 1244 if (fstat(in, &isb) != 0) { 1245 maybe_warn("couldn't stat: %s", file); 1246 close(in); 1247 return (-1); 1248 } 1249#endif 1250 1251 if (cflag == 0) { 1252#ifndef SMALL 1253 if (isb.st_nlink > 1 && fflag == 0) { 1254 maybe_warnx("%s has %d other link%s -- skipping", 1255 file, isb.st_nlink - 1, 1256 (isb.st_nlink - 1) == 1 ? "" : "s"); 1257 close(in); 1258 return (-1); 1259 } 1260 1261 if (fflag == 0 && (suff = check_suffix(file, 0)) && 1262 suff->zipped[0] != 0) { 1263 maybe_warnx("%s already has %s suffix -- unchanged", 1264 file, suff->zipped); 1265 close(in); 1266 return (-1); 1267 } 1268#endif 1269 1270 /* Add (usually) .gz to filename */ 1271 if ((size_t)snprintf(outfile, outsize, "%s%s", 1272 file, suffixes[0].zipped) >= outsize) 1273 memcpy(outfile + outsize - suffixes[0].ziplen - 1, 1274 suffixes[0].zipped, suffixes[0].ziplen + 1); 1275 1276#ifndef SMALL 1277 if (check_outfile(outfile) == 0) { 1278 close(in); 1279 return (-1); 1280 } 1281#endif 1282 } 1283 1284 if (cflag == 0) { 1285 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); 1286 if (out == -1) { 1287 maybe_warn("could not create output: %s", outfile); 1288 fclose(stdin); 1289 return (-1); 1290 } 1291#ifndef SMALL 1292 remove_file = outfile; 1293#endif 1294 } else 1295 out = STDOUT_FILENO; 1296 1297 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); 1298 1299 (void)close(in); 1300 1301 /* 1302 * If there was an error, insize will be -1. 1303 * If we compressed to stdout, just return the size. 1304 * Otherwise stat the file and check it is the correct size. 1305 * We only blow away the file if we can stat the output and it 1306 * has the expected size. 1307 */ 1308 if (cflag != 0) 1309 return (insize == -1 ? -1 : size); 1310 1311#ifndef SMALL 1312 if (fstat(out, &osb) != 0) { 1313 maybe_warn("couldn't stat: %s", outfile); 1314 goto bad_outfile; 1315 } 1316 1317 if (osb.st_size != size) { 1318 maybe_warnx("output file: %s wrong size (%ju != %ju), deleting", 1319 outfile, (uintmax_t)osb.st_size, (uintmax_t)size); 1320 goto bad_outfile; 1321 } 1322 1323 copymodes(out, &isb, outfile); 1324 remove_file = NULL; 1325#endif 1326 if (close(out) == -1) 1327 maybe_warn("couldn't close output"); 1328 1329 /* output is good, ok to delete input */ 1330 unlink_input(file, &isb); 1331 return (size); 1332 1333#ifndef SMALL 1334 bad_outfile: 1335 if (close(out) == -1) 1336 maybe_warn("couldn't close output"); 1337 1338 maybe_warnx("leaving original %s", file); 1339 unlink(outfile); 1340 return (size); 1341#endif 1342} 1343 1344/* uncompress the given file and remove the original */ 1345static off_t 1346file_uncompress(char *file, char *outfile, size_t outsize) 1347{ 1348 struct stat isb, osb; 1349 off_t size; 1350 ssize_t rbytes; 1351 unsigned char header1[4]; 1352 enum filetype method; 1353 int fd, ofd, zfd = -1; 1354#ifndef SMALL 1355 ssize_t rv; 1356 time_t timestamp = 0; 1357 char name[PATH_MAX + 1]; 1358#endif 1359 1360 /* gather the old name info */ 1361 1362 fd = open(file, O_RDONLY); 1363 if (fd < 0) { 1364 maybe_warn("can't open %s", file); 1365 goto lose; 1366 } 1367 1368 strlcpy(outfile, file, outsize); 1369 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1370 maybe_warnx("%s: unknown suffix -- ignored", file); 1371 goto lose; 1372 } 1373 1374 rbytes = read(fd, header1, sizeof header1); 1375 if (rbytes != sizeof header1) { 1376 /* we don't want to fail here. */ 1377#ifndef SMALL 1378 if (fflag) 1379 goto lose; 1380#endif 1381 if (rbytes == -1) 1382 maybe_warn("can't read %s", file); 1383 else 1384 goto unexpected_EOF; 1385 goto lose; 1386 } 1387 1388 method = file_gettype(header1); 1389#ifndef SMALL 1390 if (fflag == 0 && method == FT_UNKNOWN) { 1391 maybe_warnx("%s: not in gzip format", file); 1392 goto lose; 1393 } 1394 1395#endif 1396 1397#ifndef SMALL 1398 if (method == FT_GZIP && Nflag) { 1399 unsigned char ts[4]; /* timestamp */ 1400 1401 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1402 if (rv >= 0 && rv < (ssize_t)(sizeof ts)) 1403 goto unexpected_EOF; 1404 if (rv == -1) { 1405 if (!fflag) 1406 maybe_warn("can't read %s", file); 1407 goto lose; 1408 } 1409 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; 1410 1411 if (header1[3] & ORIG_NAME) { 1412 rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME); 1413 if (rbytes < 0) { 1414 maybe_warn("can't read %s", file); 1415 goto lose; 1416 } 1417 if (name[0] != '\0') { 1418 char *dp, *nf; 1419 1420 /* Make sure that name is NUL-terminated */ 1421 name[rbytes] = '\0'; 1422 1423 /* strip saved directory name */ 1424 nf = strrchr(name, '/'); 1425 if (nf == NULL) 1426 nf = name; 1427 else 1428 nf++; 1429 1430 /* preserve original directory name */ 1431 dp = strrchr(file, '/'); 1432 if (dp == NULL) 1433 dp = file; 1434 else 1435 dp++; 1436 snprintf(outfile, outsize, "%.*s%.*s", 1437 (int) (dp - file), 1438 file, (int) rbytes, nf); 1439 } 1440 } 1441 } 1442#endif 1443 lseek(fd, 0, SEEK_SET); 1444 1445 if (cflag == 0 || lflag) { 1446 if (fstat(fd, &isb) != 0) 1447 goto lose; 1448#ifndef SMALL 1449 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1450 maybe_warnx("%s has %d other links -- skipping", 1451 file, isb.st_nlink - 1); 1452 goto lose; 1453 } 1454 if (nflag == 0 && timestamp) 1455 isb.st_mtime = timestamp; 1456 if (check_outfile(outfile) == 0) 1457 goto lose; 1458#endif 1459 } 1460 1461 if (cflag == 0 && lflag == 0) { 1462 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1463 if (zfd == STDOUT_FILENO) { 1464 /* We won't close STDOUT_FILENO later... */ 1465 zfd = dup(zfd); 1466 close(STDOUT_FILENO); 1467 } 1468 if (zfd == -1) { 1469 maybe_warn("can't open %s", outfile); 1470 goto lose; 1471 } 1472#ifndef SMALL 1473 remove_file = outfile; 1474#endif 1475 } else 1476 zfd = STDOUT_FILENO; 1477 1478 switch (method) { 1479#ifndef NO_BZIP2_SUPPORT 1480 case FT_BZIP2: 1481 /* XXX */ 1482 if (lflag) { 1483 maybe_warnx("no -l with bzip2 files"); 1484 goto lose; 1485 } 1486 1487 size = unbzip2(fd, zfd, NULL, 0, NULL); 1488 break; 1489#endif 1490 1491#ifndef NO_COMPRESS_SUPPORT 1492 case FT_Z: { 1493 FILE *in, *out; 1494 1495 /* XXX */ 1496 if (lflag) { 1497 maybe_warnx("no -l with Lempel-Ziv files"); 1498 goto lose; 1499 } 1500 1501 if ((in = zdopen(fd)) == NULL) { 1502 maybe_warn("zdopen for read: %s", file); 1503 goto lose; 1504 } 1505 1506 out = fdopen(dup(zfd), "w"); 1507 if (out == NULL) { 1508 maybe_warn("fdopen for write: %s", outfile); 1509 fclose(in); 1510 goto lose; 1511 } 1512 1513 size = zuncompress(in, out, NULL, 0, NULL); 1514 /* need to fclose() if ferror() is true... */ 1515 if (ferror(in) | fclose(in)) { 1516 maybe_warn("failed infile fclose"); 1517 unlink(outfile); 1518 (void)fclose(out); 1519 } 1520 if (fclose(out) != 0) { 1521 maybe_warn("failed outfile fclose"); 1522 unlink(outfile); 1523 goto lose; 1524 } 1525 break; 1526 } 1527#endif 1528 1529#ifndef NO_PACK_SUPPORT 1530 case FT_PACK: 1531 if (lflag) { 1532 maybe_warnx("no -l with packed files"); 1533 goto lose; 1534 } 1535 1536 size = unpack(fd, zfd, NULL, 0, NULL); 1537 break; 1538#endif 1539 1540#ifndef NO_XZ_SUPPORT 1541 case FT_XZ: 1542 if (lflag) { 1543 maybe_warnx("no -l with xz files"); 1544 goto lose; 1545 } 1546 1547 size = unxz(fd, zfd, NULL, 0, NULL); 1548 break; 1549#endif 1550 1551#ifndef SMALL 1552 case FT_UNKNOWN: 1553 if (lflag) { 1554 maybe_warnx("no -l for unknown filetypes"); 1555 goto lose; 1556 } 1557 size = cat_fd(NULL, 0, NULL, fd); 1558 break; 1559#endif 1560 default: 1561 if (lflag) { 1562 print_list(fd, isb.st_size, outfile, isb.st_mtime); 1563 close(fd); 1564 return -1; /* XXX */ 1565 } 1566 1567 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1568 break; 1569 } 1570 1571 if (close(fd) != 0) 1572 maybe_warn("couldn't close input"); 1573 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1574 maybe_warn("couldn't close output"); 1575 1576 if (size == -1) { 1577 if (cflag == 0) 1578 unlink(outfile); 1579 maybe_warnx("%s: uncompress failed", file); 1580 return -1; 1581 } 1582 1583 /* if testing, or we uncompressed to stdout, this is all we need */ 1584#ifndef SMALL 1585 if (tflag) 1586 return size; 1587#endif 1588 /* if we are uncompressing to stdin, don't remove the file. */ 1589 if (cflag) 1590 return size; 1591 1592 /* 1593 * if we create a file... 1594 */ 1595 /* 1596 * if we can't stat the file don't remove the file. 1597 */ 1598 1599 ofd = open(outfile, O_RDWR, 0); 1600 if (ofd == -1) { 1601 maybe_warn("couldn't open (leaving original): %s", 1602 outfile); 1603 return -1; 1604 } 1605 if (fstat(ofd, &osb) != 0) { 1606 maybe_warn("couldn't stat (leaving original): %s", 1607 outfile); 1608 close(ofd); 1609 return -1; 1610 } 1611 if (osb.st_size != size) { 1612 maybe_warnx("stat gave different size: %ju != %ju (leaving original)", 1613 (uintmax_t)size, (uintmax_t)osb.st_size); 1614 close(ofd); 1615 unlink(outfile); 1616 return -1; 1617 } 1618#ifndef SMALL 1619 copymodes(ofd, &isb, outfile); 1620 remove_file = NULL; 1621#endif 1622 close(ofd); 1623 unlink_input(file, &isb); 1624 return size; 1625 1626 unexpected_EOF: 1627 maybe_warnx("%s: unexpected end of file", file); 1628 lose: 1629 if (fd != -1) 1630 close(fd); 1631 if (zfd != -1 && zfd != STDOUT_FILENO) 1632 close(fd); 1633 return -1; 1634} 1635 1636#ifndef SMALL 1637static off_t 1638cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1639{ 1640 char buf[BUFLEN]; 1641 off_t in_tot; 1642 ssize_t w; 1643 1644 in_tot = count; 1645 w = write(STDOUT_FILENO, prepend, count); 1646 if (w == -1 || (size_t)w != count) { 1647 maybe_warn("write to stdout"); 1648 return -1; 1649 } 1650 for (;;) { 1651 ssize_t rv; 1652 1653 rv = read(fd, buf, sizeof buf); 1654 if (rv == 0) 1655 break; 1656 if (rv < 0) { 1657 maybe_warn("read from fd %d", fd); 1658 break; 1659 } 1660 1661 if (write(STDOUT_FILENO, buf, rv) != rv) { 1662 maybe_warn("write to stdout"); 1663 break; 1664 } 1665 in_tot += rv; 1666 } 1667 1668 if (gsizep) 1669 *gsizep = in_tot; 1670 return (in_tot); 1671} 1672#endif 1673 1674static void 1675handle_stdin(void) 1676{ 1677 unsigned char header1[4]; 1678 off_t usize, gsize; 1679 enum filetype method; 1680 ssize_t bytes_read; 1681#ifndef NO_COMPRESS_SUPPORT 1682 FILE *in; 1683#endif 1684 1685#ifndef SMALL 1686 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1687 maybe_warnx("standard input is a terminal -- ignoring"); 1688 return; 1689 } 1690#endif 1691 1692 if (lflag) { 1693 struct stat isb; 1694 1695 /* XXX could read the whole file, etc. */ 1696 if (fstat(STDIN_FILENO, &isb) < 0) { 1697 maybe_warn("fstat"); 1698 return; 1699 } 1700 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1701 return; 1702 } 1703 1704 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1); 1705 if (bytes_read == -1) { 1706 maybe_warn("can't read stdin"); 1707 return; 1708 } else if (bytes_read != sizeof(header1)) { 1709 maybe_warnx("(stdin): unexpected end of file"); 1710 return; 1711 } 1712 1713 method = file_gettype(header1); 1714 switch (method) { 1715 default: 1716#ifndef SMALL 1717 if (fflag == 0) { 1718 maybe_warnx("unknown compression format"); 1719 return; 1720 } 1721 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO); 1722 break; 1723#endif 1724 case FT_GZIP: 1725 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1726 (char *)header1, sizeof header1, &gsize, "(stdin)"); 1727 break; 1728#ifndef NO_BZIP2_SUPPORT 1729 case FT_BZIP2: 1730 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1731 (char *)header1, sizeof header1, &gsize); 1732 break; 1733#endif 1734#ifndef NO_COMPRESS_SUPPORT 1735 case FT_Z: 1736 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1737 maybe_warnx("zopen of stdin"); 1738 return; 1739 } 1740 1741 usize = zuncompress(in, stdout, (char *)header1, 1742 sizeof header1, &gsize); 1743 fclose(in); 1744 break; 1745#endif 1746#ifndef NO_PACK_SUPPORT 1747 case FT_PACK: 1748 usize = unpack(STDIN_FILENO, STDOUT_FILENO, 1749 (char *)header1, sizeof header1, &gsize); 1750 break; 1751#endif 1752#ifndef NO_XZ_SUPPORT 1753 case FT_XZ: 1754 usize = unxz(STDIN_FILENO, STDOUT_FILENO, 1755 (char *)header1, sizeof header1, &gsize); 1756 break; 1757#endif 1758 } 1759 1760#ifndef SMALL 1761 if (vflag && !tflag && usize != -1 && gsize != -1) 1762 print_verbage(NULL, NULL, usize, gsize); 1763 if (vflag && tflag) 1764 print_test("(stdin)", usize != -1); 1765#endif 1766 1767} 1768 1769static void 1770handle_stdout(void) 1771{ 1772 off_t gsize, usize; 1773 struct stat sb; 1774 time_t systime; 1775 uint32_t mtime; 1776 int ret; 1777 1778#ifndef SMALL 1779 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1780 maybe_warnx("standard output is a terminal -- ignoring"); 1781 return; 1782 } 1783#endif 1784 /* If stdin is a file use its mtime, otherwise use current time */ 1785 ret = fstat(STDIN_FILENO, &sb); 1786 1787#ifndef SMALL 1788 if (ret < 0) { 1789 maybe_warn("Can't stat stdin"); 1790 return; 1791 } 1792#endif 1793 1794 if (S_ISREG(sb.st_mode)) 1795 mtime = (uint32_t)sb.st_mtime; 1796 else { 1797 systime = time(NULL); 1798#ifndef SMALL 1799 if (systime == -1) { 1800 maybe_warn("time"); 1801 return; 1802 } 1803#endif 1804 mtime = (uint32_t)systime; 1805 } 1806 1807 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1808#ifndef SMALL 1809 if (vflag && !tflag && usize != -1 && gsize != -1) 1810 print_verbage(NULL, NULL, usize, gsize); 1811#endif 1812} 1813 1814/* do what is asked for, for the path name */ 1815static void 1816handle_pathname(char *path) 1817{ 1818 char *opath = path, *s = NULL; 1819 ssize_t len; 1820 int slen; 1821 struct stat sb; 1822 1823 /* check for stdout/stdin */ 1824 if (path[0] == '-' && path[1] == '\0') { 1825 if (dflag) 1826 handle_stdin(); 1827 else 1828 handle_stdout(); 1829 return; 1830 } 1831 1832retry: 1833 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 && 1834 lstat(path, &sb) != 0)) { 1835 /* lets try <path>.gz if we're decompressing */ 1836 if (dflag && s == NULL && errno == ENOENT) { 1837 len = strlen(path); 1838 slen = suffixes[0].ziplen; 1839 s = malloc(len + slen + 1); 1840 if (s == NULL) 1841 maybe_err("malloc"); 1842 memcpy(s, path, len); 1843 memcpy(s + len, suffixes[0].zipped, slen + 1); 1844 path = s; 1845 goto retry; 1846 } 1847 maybe_warn("can't stat: %s", opath); 1848 goto out; 1849 } 1850 1851 if (S_ISDIR(sb.st_mode)) { 1852#ifndef SMALL 1853 if (rflag) 1854 handle_dir(path); 1855 else 1856#endif 1857 maybe_warnx("%s is a directory", path); 1858 goto out; 1859 } 1860 1861 if (S_ISREG(sb.st_mode)) 1862 handle_file(path, &sb); 1863 else 1864 maybe_warnx("%s is not a regular file", path); 1865 1866out: 1867 if (s) 1868 free(s); 1869} 1870 1871/* compress/decompress a file */ 1872static void 1873handle_file(char *file, struct stat *sbp) 1874{ 1875 off_t usize, gsize; 1876 char outfile[PATH_MAX]; 1877 1878 infile = file; 1879 if (dflag) { 1880 usize = file_uncompress(file, outfile, sizeof(outfile)); 1881#ifndef SMALL 1882 if (vflag && tflag) 1883 print_test(file, usize != -1); 1884#endif 1885 if (usize == -1) 1886 return; 1887 gsize = sbp->st_size; 1888 } else { 1889 gsize = file_compress(file, outfile, sizeof(outfile)); 1890 if (gsize == -1) 1891 return; 1892 usize = sbp->st_size; 1893 } 1894 1895 1896#ifndef SMALL 1897 if (vflag && !tflag) 1898 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1899#endif 1900} 1901 1902#ifndef SMALL 1903/* this is used with -r to recursively descend directories */ 1904static void 1905handle_dir(char *dir) 1906{ 1907 char *path_argv[2]; 1908 FTS *fts; 1909 FTSENT *entry; 1910 1911 path_argv[0] = dir; 1912 path_argv[1] = 0; 1913 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 1914 if (fts == NULL) { 1915 warn("couldn't fts_open %s", dir); 1916 return; 1917 } 1918 1919 while ((entry = fts_read(fts))) { 1920 switch(entry->fts_info) { 1921 case FTS_D: 1922 case FTS_DP: 1923 continue; 1924 1925 case FTS_DNR: 1926 case FTS_ERR: 1927 case FTS_NS: 1928 maybe_warn("%s", entry->fts_path); 1929 continue; 1930 case FTS_F: 1931 handle_file(entry->fts_path, entry->fts_statp); 1932 } 1933 } 1934 (void)fts_close(fts); 1935} 1936#endif 1937 1938/* print a ratio - size reduction as a fraction of uncompressed size */ 1939static void 1940print_ratio(off_t in, off_t out, FILE *where) 1941{ 1942 int percent10; /* 10 * percent */ 1943 off_t diff; 1944 char buff[8]; 1945 int len; 1946 1947 diff = in - out/2; 1948 if (diff <= 0) 1949 /* 1950 * Output is more than double size of input! print -99.9% 1951 * Quite possibly we've failed to get the original size. 1952 */ 1953 percent10 = -999; 1954 else { 1955 /* 1956 * We only need 12 bits of result from the final division, 1957 * so reduce the values until a 32bit division will suffice. 1958 */ 1959 while (in > 0x100000) { 1960 diff >>= 1; 1961 in >>= 1; 1962 } 1963 if (in != 0) 1964 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 1965 else 1966 percent10 = 0; 1967 } 1968 1969 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 1970 /* Move the '.' to before the last digit */ 1971 buff[len - 1] = buff[len - 2]; 1972 buff[len - 2] = '.'; 1973 fprintf(where, "%5s%%", buff); 1974} 1975 1976#ifndef SMALL 1977/* print compression statistics, and the new name (if there is one!) */ 1978static void 1979print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 1980{ 1981 if (file) 1982 fprintf(stderr, "%s:%s ", file, 1983 strlen(file) < 7 ? "\t\t" : "\t"); 1984 print_ratio(usize, gsize, stderr); 1985 if (nfile) 1986 fprintf(stderr, " -- replaced with %s", nfile); 1987 fprintf(stderr, "\n"); 1988 fflush(stderr); 1989} 1990 1991/* print test results */ 1992static void 1993print_test(const char *file, int ok) 1994{ 1995 1996 if (exit_value == 0 && ok == 0) 1997 exit_value = 1; 1998 fprintf(stderr, "%s:%s %s\n", file, 1999 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 2000 fflush(stderr); 2001} 2002#endif 2003 2004/* print a file's info ala --list */ 2005/* eg: 2006 compressed uncompressed ratio uncompressed_name 2007 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 2008*/ 2009static void 2010print_list(int fd, off_t out, const char *outfile, time_t ts) 2011{ 2012 static int first = 1; 2013#ifndef SMALL 2014 static off_t in_tot, out_tot; 2015 uint32_t crc = 0; 2016#endif 2017 off_t in = 0, rv; 2018 2019 if (first) { 2020#ifndef SMALL 2021 if (vflag) 2022 printf("method crc date time "); 2023#endif 2024 if (qflag == 0) 2025 printf(" compressed uncompressed " 2026 "ratio uncompressed_name\n"); 2027 } 2028 first = 0; 2029 2030 /* print totals? */ 2031#ifndef SMALL 2032 if (fd == -1) { 2033 in = in_tot; 2034 out = out_tot; 2035 } else 2036#endif 2037 { 2038 /* read the last 4 bytes - this is the uncompressed size */ 2039 rv = lseek(fd, (off_t)(-8), SEEK_END); 2040 if (rv != -1) { 2041 unsigned char buf[8]; 2042 uint32_t usize; 2043 2044 rv = read(fd, (char *)buf, sizeof(buf)); 2045 if (rv == -1) 2046 maybe_warn("read of uncompressed size"); 2047 else if (rv != sizeof(buf)) 2048 maybe_warnx("read of uncompressed size"); 2049 2050 else { 2051 usize = buf[4] | buf[5] << 8 | 2052 buf[6] << 16 | buf[7] << 24; 2053 in = (off_t)usize; 2054#ifndef SMALL 2055 crc = buf[0] | buf[1] << 8 | 2056 buf[2] << 16 | buf[3] << 24; 2057#endif 2058 } 2059 } 2060 } 2061 2062#ifndef SMALL 2063 if (vflag && fd == -1) 2064 printf(" "); 2065 else if (vflag) { 2066 char *date = ctime(&ts); 2067 2068 /* skip the day, 1/100th second, and year */ 2069 date += 4; 2070 date[12] = 0; 2071 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 2072 } 2073 in_tot += in; 2074 out_tot += out; 2075#else 2076 (void)&ts; /* XXX */ 2077#endif 2078 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 2079 print_ratio(in, out, stdout); 2080 printf(" %s\n", outfile); 2081} 2082 2083/* display the usage of NetBSD gzip */ 2084static void 2085usage(void) 2086{ 2087 2088 fprintf(stderr, "%s\n", gzip_version); 2089 fprintf(stderr, 2090#ifdef SMALL 2091 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n", 2092#else 2093 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n" 2094 " -1 --fast fastest (worst) compression\n" 2095 " -2 .. -8 set compression level\n" 2096 " -9 --best best (slowest) compression\n" 2097 " -c --stdout write to stdout, keep original files\n" 2098 " --to-stdout\n" 2099 " -d --decompress uncompress files\n" 2100 " --uncompress\n" 2101 " -f --force force overwriting & compress links\n" 2102 " -h --help display this help\n" 2103 " -k --keep don't delete input files during operation\n" 2104 " -l --list list compressed file contents\n" 2105 " -N --name save or restore original file name and time stamp\n" 2106 " -n --no-name don't save original file name or time stamp\n" 2107 " -q --quiet output no warnings\n" 2108 " -r --recursive recursively compress files in directories\n" 2109 " -S .suf use suffix .suf instead of .gz\n" 2110 " --suffix .suf\n" 2111 " -t --test test compressed file\n" 2112 " -V --version display program version\n" 2113 " -v --verbose print extra statistics\n", 2114#endif 2115 getprogname()); 2116 exit(0); 2117} 2118 2119#ifndef SMALL 2120/* display the license information of FreeBSD gzip */ 2121static void 2122display_license(void) 2123{ 2124 2125 fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version); 2126 fprintf(stderr, "%s\n", gzip_copyright); 2127 exit(0); 2128} 2129#endif 2130 2131/* display the version of NetBSD gzip */ 2132static void 2133display_version(void) 2134{ 2135 2136 fprintf(stderr, "%s\n", gzip_version); 2137 exit(0); 2138} 2139 2140#ifndef NO_BZIP2_SUPPORT 2141#include "unbzip2.c" 2142#endif 2143#ifndef NO_COMPRESS_SUPPORT 2144#include "zuncompress.c" 2145#endif 2146#ifndef NO_PACK_SUPPORT 2147#include "unpack.c" 2148#endif 2149#ifndef NO_XZ_SUPPORT 2150#include "unxz.c" 2151#endif 2152 2153static ssize_t 2154read_retry(int fd, void *buf, size_t sz) 2155{ 2156 char *cp = buf; 2157 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2158 2159 while (left > 0) { 2160 ssize_t ret; 2161 2162 ret = read(fd, cp, left); 2163 if (ret == -1) { 2164 return ret; 2165 } else if (ret == 0) { 2166 break; /* EOF */ 2167 } 2168 cp += ret; 2169 left -= ret; 2170 } 2171 2172 return sz - left; 2173} 2174