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