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