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