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