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