1 /* $NetBSD: gzip.c,v 1.48 2004/06/06 12:28:52 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1997, 1998, 2003, 2004 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #ifndef lint 33 __COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004 Matthew R. Green\n\ 34 All rights reserved.\n"); 35 __RCSID("$NetBSD: gzip.c,v 1.48 2004/06/06 12:28:52 mrg Exp $"); 36 #endif /* not lint */ 37 38 /* 39 * gzip.c -- GPL free gzip using zlib. 40 * 41 * TODO: 42 * - handle .taz/.tgz files? 43 * - use mmap where possible 44 * - handle some signals better (remove outfile?) 45 * - make bzip2/compress -v/-t/-l support work as well as possible 46 */ 47 48 #include <sys/param.h> 49 #include <sys/stat.h> 50 #include <sys/time.h> 51 52 #include <unistd.h> 53 #include <stdio.h> 54 #include <string.h> 55 #include <stdlib.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <fcntl.h> 59 #include <zlib.h> 60 #include <fts.h> 61 #include <libgen.h> 62 #include <stdarg.h> 63 #include <getopt.h> 64 65 /* what type of file are we dealing with */ 66 enum filetype { 67 FT_GZIP, 68 #ifndef NO_BZIP2_SUPPORT 69 FT_BZIP2, 70 #endif 71 #ifndef NO_COMPRESS_SUPPORT 72 FT_Z, 73 #endif 74 FT_LAST, 75 FT_UNKNOWN 76 }; 77 78 #ifndef NO_BZIP2_SUPPORT 79 #include <bzlib.h> 80 81 #define BZ2_SUFFIX ".bz2" 82 #define BZIP2_MAGIC "\102\132\150" 83 #endif 84 85 #ifndef NO_COMPRESS_SUPPORT 86 #define Z_SUFFIX ".Z" 87 #define Z_MAGIC "\037\235" 88 #endif 89 90 #define GZ_SUFFIX ".gz" 91 92 #define BUFLEN (64 * 1024) 93 94 #define GZIP_MAGIC0 0x1F 95 #define GZIP_MAGIC1 0x8B 96 #define GZIP_OMAGIC1 0x9E 97 98 #define GZIP_TIMESTAMP (off_t)4 99 #define GZIP_ORIGNAME (off_t)10 100 101 #define HEAD_CRC 0x02 102 #define EXTRA_FIELD 0x04 103 #define ORIG_NAME 0x08 104 #define COMMENT 0x10 105 106 #define OS_CODE 3 /* Unix */ 107 108 static const char gzip_version[] = "NetBSD gzip 20040524"; 109 110 static int cflag; /* stdout mode */ 111 static int dflag; /* decompress mode */ 112 static int lflag; /* list mode */ 113 static int numflag = 6; /* gzip -1..-9 value */ 114 115 #ifndef SMALL 116 static int fflag; /* force mode */ 117 static int nflag; /* don't save name/timestamp */ 118 static int Nflag; /* don't restore name/timestamp */ 119 static int qflag; /* quiet mode */ 120 static int rflag; /* recursive mode */ 121 static int tflag; /* test */ 122 static char *Sflag; 123 static int vflag; /* verbose mode */ 124 #else 125 #define qflag 0 126 #endif 127 128 static int exit_value = 0; /* exit value */ 129 130 static const char *suffix; 131 #define suffix_len (strlen(suffix) + 1) /* len + nul */ 132 static char *infile; /* name of file coming in */ 133 134 static void maybe_err(const char *fmt, ...); 135 static void maybe_errx(const char *fmt, ...); 136 static void maybe_warn(const char *fmt, ...); 137 static void maybe_warnx(const char *fmt, ...); 138 static enum filetype file_gettype(u_char *); 139 static int check_outfile(const char *outfile, struct stat *sb); 140 static off_t gz_compress(FILE *, int, off_t *, const char *, time_t); 141 static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); 142 static off_t file_compress(char *, char *, size_t); 143 static off_t file_uncompress(char *, char *, size_t); 144 static void handle_pathname(char *); 145 static void handle_file(char *, struct stat *); 146 static void handle_stdin(void); 147 static void handle_stdout(void); 148 static void print_ratio(off_t, off_t, FILE *); 149 static void print_list(int fd, off_t, const char *, time_t); 150 static void usage(void); 151 static void display_version(void); 152 153 #ifndef SMALL 154 static void prepend_gzip(char *, int *, char ***); 155 static void handle_dir(char *, struct stat *); 156 static void print_verbage(char *, char *, off_t, off_t); 157 static void print_test(const char *, int); 158 static void copymodes(const char *, struct stat *); 159 #endif 160 161 #ifndef NO_BZIP2_SUPPORT 162 static off_t unbzip2(int, int, char *, size_t, off_t *); 163 #endif 164 165 #ifndef NO_COMPRESS_SUPPORT 166 static FILE *zopen(const char *, FILE *); 167 static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); 168 #endif 169 170 int main(int, char *p[]); 171 172 #ifdef SMALL 173 #define getopt_long(a,b,c,d,e) getopt(a,b,c) 174 #else 175 static const struct option longopts[] = { 176 { "stdout", no_argument, 0, 'c' }, 177 { "to-stdout", no_argument, 0, 'c' }, 178 { "decompress", no_argument, 0, 'd' }, 179 { "uncompress", no_argument, 0, 'd' }, 180 { "force", no_argument, 0, 'f' }, 181 { "help", no_argument, 0, 'h' }, 182 { "list", no_argument, 0, 'l' }, 183 { "no-name", no_argument, 0, 'n' }, 184 { "name", no_argument, 0, 'N' }, 185 { "quiet", no_argument, 0, 'q' }, 186 { "recursive", no_argument, 0, 'r' }, 187 { "suffix", required_argument, 0, 'S' }, 188 { "test", no_argument, 0, 't' }, 189 { "verbose", no_argument, 0, 'v' }, 190 { "version", no_argument, 0, 'V' }, 191 { "fast", no_argument, 0, '1' }, 192 { "best", no_argument, 0, '9' }, 193 #if 0 194 /* 195 * This is what else GNU gzip implements. --ascii isn't useful 196 * on NetBSD, and I don't care to have a --license. 197 */ 198 { "ascii", no_argument, 0, 'a' }, 199 { "license", no_argument, 0, 'L' }, 200 #endif 201 { NULL, no_argument, 0, 0 }, 202 }; 203 #endif 204 205 int 206 main(int argc, char **argv) 207 { 208 const char *progname = getprogname(); 209 #ifndef SMALL 210 char *gzip; 211 #endif 212 int ch; 213 214 /* XXX set up signals */ 215 216 suffix = GZ_SUFFIX;; 217 218 #ifndef SMALL 219 if ((gzip = getenv("GZIP")) != NULL) 220 prepend_gzip(gzip, &argc, &argv); 221 #endif 222 223 /* 224 * XXX 225 * handle being called `gunzip', `zcat' and `gzcat' 226 */ 227 if (strcmp(progname, "gunzip") == 0) 228 dflag = 1; 229 else if (strcmp(progname, "zcat") == 0 || 230 strcmp(progname, "gzcat") == 0) 231 dflag = cflag = 1; 232 233 #ifdef SMALL 234 #define OPT_LIST "cdhHltV123456789" 235 #else 236 #define OPT_LIST "cdfhHlnNqrS:tvV123456789" 237 #endif 238 239 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) 240 switch (ch) { 241 case 'c': 242 cflag = 1; 243 break; 244 case 'd': 245 dflag = 1; 246 break; 247 case 'l': 248 lflag = 1; 249 dflag = 1; 250 break; 251 case 'V': 252 display_version(); 253 /* NOTREACHED */ 254 case '1': case '2': case '3': 255 case '4': case '5': case '6': 256 case '7': case '8': case '9': 257 numflag = ch - '0'; 258 break; 259 #ifndef SMALL 260 case 'f': 261 fflag = 1; 262 break; 263 case 'n': 264 nflag = 1; 265 Nflag = 0; 266 break; 267 case 'N': 268 nflag = 0; 269 Nflag = 1; 270 break; 271 case 'q': 272 qflag = 1; 273 break; 274 case 'r': 275 rflag = 1; 276 break; 277 case 'S': 278 Sflag = optarg; 279 break; 280 case 't': 281 cflag = 1; 282 tflag = 1; 283 dflag = 1; 284 break; 285 case 'v': 286 vflag = 1; 287 break; 288 #endif 289 default: 290 usage(); 291 /* NOTREACHED */ 292 } 293 argv += optind; 294 argc -= optind; 295 296 if (argc == 0) { 297 if (dflag) /* stdin mode */ 298 handle_stdin(); 299 else /* stdout mode */ 300 handle_stdout(); 301 } else { 302 do { 303 handle_pathname(argv[0]); 304 } while (*++argv); 305 } 306 #ifndef SMALL 307 if (qflag == 0 && lflag && argc > 1) 308 print_list(-1, 0, "(totals)", 0); 309 #endif 310 exit(exit_value); 311 } 312 313 /* maybe print a warning */ 314 void 315 maybe_warn(const char *fmt, ...) 316 { 317 va_list ap; 318 319 if (qflag == 0) { 320 va_start(ap, fmt); 321 vwarn(fmt, ap); 322 va_end(ap); 323 } 324 if (exit_value == 0) 325 exit_value = 1; 326 } 327 328 /* ... without an errno. */ 329 void 330 maybe_warnx(const char *fmt, ...) 331 { 332 va_list ap; 333 334 if (qflag == 0) { 335 va_start(ap, fmt); 336 vwarnx(fmt, ap); 337 va_end(ap); 338 } 339 if (exit_value == 0) 340 exit_value = 1; 341 } 342 343 /* maybe print an error */ 344 void 345 maybe_err(const char *fmt, ...) 346 { 347 va_list ap; 348 349 if (qflag == 0) { 350 va_start(ap, fmt); 351 vwarn(fmt, ap); 352 va_end(ap); 353 } 354 exit(2); 355 } 356 357 /* ... without an errno. */ 358 void 359 maybe_errx(const char *fmt, ...) 360 { 361 va_list ap; 362 363 if (qflag == 0) { 364 va_start(ap, fmt); 365 vwarnx(fmt, ap); 366 va_end(ap); 367 } 368 exit(2); 369 } 370 371 #ifndef SMALL 372 /* split up $GZIP and prepend it to the argument list */ 373 static void 374 prepend_gzip(char *gzip, int *argc, char ***argv) 375 { 376 char *s, **nargv, **ac; 377 int nenvarg = 0, i; 378 379 /* scan how many arguments there are */ 380 for (s = gzip; *s; s++) { 381 if (*s == ' ' || *s == '\t') 382 continue; 383 nenvarg++; 384 for (; *s; s++) 385 if (*s == ' ' || *s == '\t') 386 break; 387 if (*s == 0x0) 388 break; 389 } 390 /* punt early */ 391 if (nenvarg == 0) 392 return; 393 394 *argc += nenvarg; 395 ac = *argv; 396 397 nargv = (char **)malloc((*argc + 1) * sizeof(char *)); 398 if (nargv == NULL) 399 maybe_err("malloc"); 400 401 /* stash this away */ 402 *argv = nargv; 403 404 /* copy the program name first */ 405 i = 0; 406 nargv[i++] = *(ac++); 407 408 /* take a copy of $GZIP and add it to the array */ 409 s = strdup(gzip); 410 if (s == NULL) 411 maybe_err("strdup"); 412 for (; *s; s++) { 413 if (*s == ' ' || *s == '\t') 414 continue; 415 nargv[i++] = s; 416 for (; *s; s++) 417 if (*s == ' ' || *s == '\t') { 418 *s = 0; 419 break; 420 } 421 } 422 423 /* copy the original arguments and a NULL */ 424 while (*ac) 425 nargv[i++] = *(ac++); 426 nargv[i] = NULL; 427 } 428 #endif 429 430 /* compress input to output then close both files */ 431 static off_t 432 gz_compress(FILE *in, int out, off_t *gsizep, const char *origname, time_t mtime) 433 { 434 z_stream z; 435 char inbuf[BUFLEN], outbuf[BUFLEN]; 436 off_t in_tot = 0, out_tot = 0; 437 ssize_t in_size; 438 char *str; 439 int i, error; 440 uLong crc; 441 442 i = asprintf(&str, "%c%c%c%c%c%c%c%c%c%c%s", 443 GZIP_MAGIC0, GZIP_MAGIC1, 444 Z_DEFLATED, origname ? ORIG_NAME : 0, 445 (int)mtime & 0xff, 446 (int)(mtime >> 8) & 0xff, 447 (int)(mtime >> 16) & 0xff, 448 (int)(mtime >> 24) & 0xff, 449 0, OS_CODE, origname ? origname : ""); 450 if (i == -1) 451 maybe_err("asprintf"); 452 if (origname) 453 i++; 454 if (write(out, str, i) != i) { 455 maybe_warn("write"); 456 in_tot = -1; 457 goto out; 458 } 459 free(str); 460 461 memset(&z, 0, sizeof z); 462 z.next_out = outbuf; 463 z.avail_out = sizeof outbuf; 464 z.zalloc = Z_NULL; 465 z.zfree = Z_NULL; 466 z.opaque = 0; 467 468 error = deflateInit2(&z, numflag, Z_DEFLATED, 469 -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); 470 if (error != Z_OK) { 471 maybe_warnx("deflateInit2 failed"); 472 in_tot = -1; 473 goto out; 474 } 475 476 crc = crc32(0L, Z_NULL, 0); 477 for (;;) { 478 if (z.avail_out == 0) { 479 if (write(out, outbuf, sizeof outbuf) != sizeof outbuf) { 480 maybe_warn("write"); 481 in_tot = -1; 482 goto out; 483 } 484 485 out_tot += sizeof outbuf; 486 z.next_out = outbuf; 487 z.avail_out = sizeof outbuf; 488 } 489 490 if (z.avail_in == 0) { 491 in_size = fread(inbuf, 1, sizeof inbuf, in); 492 if (ferror(in)) { 493 maybe_warn("fread"); 494 in_tot = -1; 495 goto out; 496 } 497 if (in_size == 0) 498 break; 499 500 crc = crc32(crc, (const Bytef *)inbuf, (unsigned)in_size); 501 in_tot += in_size; 502 z.next_in = inbuf; 503 z.avail_in = in_size; 504 } 505 506 error = deflate(&z, Z_NO_FLUSH); 507 if (error != Z_OK && error != Z_STREAM_END) { 508 maybe_warnx("deflate failed"); 509 in_tot = -1; 510 goto out; 511 } 512 } 513 514 /* clean up */ 515 for (;;) { 516 size_t len; 517 518 error = deflate(&z, Z_FINISH); 519 if (error != Z_OK && error != Z_STREAM_END) { 520 maybe_warnx("deflate failed"); 521 in_tot = -1; 522 goto out; 523 } 524 525 len = sizeof outbuf - z.avail_out; 526 527 if (write(out, outbuf, len) != len) { 528 maybe_warn("write"); 529 out_tot = -1; 530 goto out; 531 } 532 out_tot += len; 533 z.next_out = outbuf; 534 z.avail_out = sizeof outbuf; 535 536 if (error == Z_STREAM_END) 537 break; 538 } 539 540 if (deflateEnd(&z) != Z_OK) { 541 maybe_warnx("deflateEnd failed"); 542 in_tot = -1; 543 goto out; 544 } 545 546 i = asprintf(&str, "%c%c%c%c%c%c%c%c", 547 (int)crc & 0xff, 548 (int)(crc >> 8) & 0xff, 549 (int)(crc >> 16) & 0xff, 550 (int)(crc >> 24) & 0xff, 551 (int)in_tot & 0xff, 552 (int)(in_tot >> 8) & 0xff, 553 (int)(in_tot >> 16) & 0xff, 554 (int)(in_tot >> 24) & 0xff); 555 if (i != 8) 556 maybe_err("asprintf"); 557 if (write(out, str, i) != i) { 558 maybe_warn("write"); 559 in_tot = -1; 560 } 561 free(str); 562 563 out: 564 if (gsizep) 565 *gsizep = out_tot; 566 return in_tot; 567 } 568 569 /* 570 * uncompress input to output then close the input. return the 571 * uncompressed size written, and put the compressed sized read 572 * into `*gsizep'. 573 */ 574 static off_t 575 gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, 576 const char *filename) 577 { 578 z_stream z; 579 char outbuf[BUFLEN], inbuf[BUFLEN]; 580 off_t out_tot, in_tot; 581 enum { 582 GZSTATE_MAGIC0, 583 GZSTATE_MAGIC1, 584 GZSTATE_METHOD, 585 GZSTATE_FLAGS, 586 GZSTATE_SKIPPING, 587 GZSTATE_EXTRA, 588 GZSTATE_EXTRA2, 589 GZSTATE_EXTRA3, 590 GZSTATE_ORIGNAME, 591 GZSTATE_COMMENT, 592 GZSTATE_HEAD_CRC1, 593 GZSTATE_HEAD_CRC2, 594 GZSTATE_INIT, 595 GZSTATE_READ 596 } state = GZSTATE_MAGIC0; 597 int flags = 0, skip_count = 0; 598 int error, done_reading = 0; 599 600 #define ADVANCE() { z.next_in++; z.avail_in--; } 601 602 memset(&z, 0, sizeof z); 603 z.avail_in = prelen; 604 z.next_in = pre; 605 z.avail_out = sizeof outbuf; 606 z.next_out = outbuf; 607 z.zalloc = NULL; 608 z.zfree = NULL; 609 z.opaque = 0; 610 611 in_tot = prelen; 612 out_tot = 0; 613 614 for (;;) { 615 if (z.avail_in == 0 && done_reading == 0) { 616 size_t in_size = read(in, inbuf, BUFLEN); 617 618 if (in_size == -1) { 619 #ifndef SMALL 620 if (tflag && vflag) 621 print_test(filename, 0); 622 #endif 623 maybe_warn("failed to read stdin\n"); 624 out_tot = -1; 625 goto stop; 626 } else if (in_size == 0) 627 done_reading = 1; 628 629 z.avail_in = in_size; 630 z.next_in = inbuf; 631 632 in_tot += in_size; 633 } 634 switch (state) { 635 case GZSTATE_MAGIC0: 636 if (*z.next_in != GZIP_MAGIC0) { 637 maybe_warn("input not gziped\n"); 638 out_tot = -1; 639 goto stop; 640 } 641 ADVANCE(); 642 state++; 643 break; 644 645 case GZSTATE_MAGIC1: 646 if (*z.next_in != GZIP_MAGIC1 && 647 *z.next_in != GZIP_OMAGIC1) { 648 maybe_warn("input not gziped\n"); 649 out_tot = -1; 650 goto stop; 651 } 652 ADVANCE(); 653 state++; 654 break; 655 656 case GZSTATE_METHOD: 657 if (*z.next_in != Z_DEFLATED) { 658 maybe_warn("unknown compression method\n"); 659 out_tot = -1; 660 goto stop; 661 } 662 ADVANCE(); 663 state++; 664 break; 665 666 case GZSTATE_FLAGS: 667 flags = *z.next_in; 668 ADVANCE(); 669 skip_count = 6; 670 state++; 671 break; 672 673 case GZSTATE_SKIPPING: 674 if (skip_count > 0) { 675 skip_count--; 676 ADVANCE(); 677 } else 678 state++; 679 break; 680 681 case GZSTATE_EXTRA: 682 if ((flags & EXTRA_FIELD) == 0) { 683 state = GZSTATE_ORIGNAME; 684 break; 685 } 686 skip_count = *z.next_in; 687 ADVANCE(); 688 state++; 689 break; 690 691 case GZSTATE_EXTRA2: 692 skip_count |= ((*z.next_in) << 8); 693 ADVANCE(); 694 state++; 695 break; 696 697 case GZSTATE_EXTRA3: 698 if (skip_count > 0) { 699 skip_count--; 700 ADVANCE(); 701 } else 702 state++; 703 break; 704 705 case GZSTATE_ORIGNAME: 706 if ((flags & ORIG_NAME) == 0) { 707 state++; 708 break; 709 } 710 if (*z.next_in == 0) 711 state++; 712 ADVANCE(); 713 break; 714 715 case GZSTATE_COMMENT: 716 if ((flags & COMMENT) == 0) { 717 state++; 718 break; 719 } 720 if (*z.next_in == 0) 721 state++; 722 ADVANCE(); 723 break; 724 725 case GZSTATE_HEAD_CRC1: 726 if (flags & HEAD_CRC) 727 skip_count = 2; 728 else 729 skip_count = 0; 730 state++; 731 break; 732 733 case GZSTATE_HEAD_CRC2: 734 if (skip_count > 0) { 735 skip_count--; 736 ADVANCE(); 737 } else 738 state++; 739 break; 740 741 case GZSTATE_INIT: 742 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 743 maybe_warn("failed to inflateInit\n"); 744 out_tot = -1; 745 goto stop; 746 } 747 state++; 748 break; 749 750 case GZSTATE_READ: 751 error = inflate(&z, Z_FINISH); 752 if (error == Z_STREAM_END || error == Z_BUF_ERROR) { 753 size_t wr = BUFLEN - z.avail_out; 754 755 /* 756 * If there's nothing to output, we must 757 * have an error... 758 */ 759 if (wr == 0) { 760 out_tot = -1; 761 goto stop; 762 } 763 if ( 764 #ifndef SMALL 765 /* don't write anything with -t */ 766 tflag == 0 && 767 #endif 768 write(out, outbuf, wr) != wr) { 769 maybe_warn("error writing " 770 "to output\n"); 771 out_tot = -1; 772 goto stop; 773 } 774 775 out_tot += wr; 776 777 if (error == Z_STREAM_END) 778 goto stop; 779 780 z.next_out = outbuf; 781 z.avail_out = BUFLEN; 782 783 break; 784 } 785 if (error < 0) { 786 maybe_warnx("decompression error\n"); 787 out_tot = -1; 788 goto stop; 789 } 790 break; 791 } 792 continue; 793 stop: 794 break; 795 } 796 if (state > GZSTATE_INIT) 797 inflateEnd(&z); 798 799 #ifndef SMALL 800 if (tflag && vflag) 801 print_test(filename, out_tot != -1); 802 #endif 803 804 if (gsizep) 805 *gsizep = in_tot; 806 return (out_tot); 807 } 808 809 #ifndef SMALL 810 /* 811 * set the owner, mode, flags & utimes for a file 812 */ 813 static void 814 copymodes(const char *file, struct stat *sbp) 815 { 816 struct timeval times[2]; 817 818 /* 819 * If we have no info on the input, give this file some 820 * default values and return.. 821 */ 822 if (sbp == NULL) { 823 mode_t mask = umask(022); 824 825 (void)chmod(file, DEFFILEMODE & ~mask); 826 (void)umask(mask); 827 return; 828 } 829 830 /* if the chown fails, remove set-id bits as-per compress(1) */ 831 if (chown(file, sbp->st_uid, sbp->st_gid) < 0) { 832 if (errno != EPERM) 833 maybe_warn("couldn't chown: %s", file); 834 sbp->st_mode &= ~(S_ISUID|S_ISGID); 835 } 836 837 /* we only allow set-id and the 9 normal permission bits */ 838 sbp->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 839 if (chmod(file, sbp->st_mode) < 0) 840 maybe_warn("couldn't chmod: %s", file); 841 842 /* only try flags if they exist already */ 843 if (sbp->st_flags != 0 && chflags(file, sbp->st_flags) < 0) 844 maybe_warn("couldn't chflags: %s", file); 845 846 TIMESPEC_TO_TIMEVAL(×[0], &sbp->st_atimespec); 847 TIMESPEC_TO_TIMEVAL(×[1], &sbp->st_mtimespec); 848 if (utimes(file, times) < 0) 849 maybe_warn("couldn't utimes: %s", file); 850 } 851 #endif 852 853 /* what sort of file is this? */ 854 static enum filetype 855 file_gettype(u_char *buf) 856 { 857 858 if (buf[0] == GZIP_MAGIC0 && 859 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 860 return FT_GZIP; 861 else 862 #ifndef NO_BZIP2_SUPPORT 863 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 864 buf[3] >= '0' && buf[3] <= '9') 865 return FT_BZIP2; 866 else 867 #endif 868 #ifndef NO_COMPRESS_SUPPORT 869 if (memcmp(buf, Z_MAGIC, 2) == 0) 870 return FT_Z; 871 else 872 #endif 873 return FT_UNKNOWN; 874 } 875 876 #ifndef SMALL 877 /* check the outfile is OK. */ 878 static int 879 check_outfile(const char *outfile, struct stat *sb) 880 { 881 int ok = 1; 882 883 if (fflag == 0 && lflag == 0 && stat(outfile, sb) == 0) { 884 if (isatty(STDIN_FILENO)) { 885 char ans[10] = { 'n', '\0' }; /* default */ 886 887 fprintf(stderr, "%s already exists -- do you wish to " 888 "overwrite (y or n)? " , outfile); 889 (void)fgets(ans, sizeof(ans) - 1, stdin); 890 if (ans[0] != 'y' && ans[0] != 'Y') { 891 fprintf(stderr, "\tnot overwritting\n"); 892 ok = 0; 893 } else 894 unlink(outfile); 895 } else { 896 maybe_warnx("%s already exists -- skipping", outfile); 897 ok = 0; 898 } 899 } 900 return ok; 901 } 902 #endif 903 904 /* 905 * compress the given file: create a corresponding .gz file and remove the 906 * original. 907 */ 908 static off_t 909 file_compress(char *file, char *outfile, size_t outsize) 910 { 911 FILE *in; 912 int out; 913 struct stat isb, osb; 914 off_t size; 915 #ifndef SMALL 916 u_int32_t mtime = 0; 917 char *savename; 918 #endif 919 920 if (cflag == 0) { 921 (void)strncpy(outfile, file, outsize - suffix_len); 922 outfile[outsize - suffix_len] = '\0'; 923 (void)strlcat(outfile, suffix, outsize); 924 925 #ifndef SMALL 926 if (check_outfile(outfile, &osb) == 0) 927 goto lose; 928 929 if (stat(file, &isb) == 0) { 930 if (isb.st_nlink > 1 && fflag == 0) { 931 maybe_warnx("%s has %d other link%s -- " 932 "skipping", file, isb.st_nlink - 1, 933 isb.st_nlink == 1 ? "" : "s"); 934 goto lose; 935 } 936 if (nflag == 0) 937 mtime = (u_int32_t)isb.st_mtime; 938 } 939 #endif 940 } 941 in = fopen(file, "r"); 942 if (in == NULL) { 943 maybe_warn("can't fopen %s", file); 944 goto lose; 945 } 946 947 #ifndef SMALL 948 if (nflag == 0) 949 savename = basename(file); 950 else 951 savename = NULL; 952 #endif 953 if (cflag == 0) { 954 out = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 955 if (out == -1) { 956 maybe_warn("could not create output: %s", outfile); 957 goto lose; 958 } 959 } else 960 out = STDOUT_FILENO; 961 962 #ifdef SMALL 963 gz_compress(in, out, NULL, NULL, 0); 964 #else 965 gz_compress(in, out, NULL, savename, mtime); 966 #endif 967 968 (void)fclose(in); 969 970 /* 971 * if we compressed to stdout, we don't know the size and 972 * we don't know the new file name, punt. if we can't stat 973 * the file, whine, otherwise set the size from the stat 974 * buffer. we only blow away the file if we can stat the 975 * output, just in case. 976 */ 977 if (cflag == 0) { 978 if (close(out) == -1) 979 maybe_warn("couldn't close ouput"); 980 981 if (stat(outfile, &osb) < 0) { 982 maybe_warn("couldn't stat: %s", outfile); 983 maybe_warnx("leaving original %s", file); 984 size = 0; 985 } else { 986 unlink(file); 987 size = osb.st_size; 988 } 989 #ifndef SMALL 990 copymodes(outfile, &isb); 991 #endif 992 } else { 993 lose: 994 size = -1; 995 } 996 997 return (size); 998 } 999 1000 /* uncompress the given file and remove the original */ 1001 static off_t 1002 file_uncompress(char *file, char *outfile, size_t outsize) 1003 { 1004 struct stat isb, osb; 1005 char *s; 1006 off_t size; 1007 ssize_t len = strlen(file); 1008 unsigned char header1[4], name[PATH_MAX + 1]; 1009 enum filetype method; 1010 int fd, zfd; 1011 #ifndef SMALL 1012 time_t timestamp = 0; 1013 #endif 1014 1015 /* gather the old name info */ 1016 1017 fd = open(file, O_RDONLY); 1018 if (fd < 0) { 1019 maybe_warn("can't open %s", file); 1020 goto lose; 1021 } 1022 if (read(fd, header1, sizeof header1) != sizeof header1) { 1023 /* we don't want to fail here. */ 1024 #ifndef SMALL 1025 if (fflag) 1026 goto lose_close_it; 1027 #endif 1028 maybe_warn("can't read %s", file); 1029 goto lose; 1030 } 1031 1032 method = file_gettype(header1); 1033 1034 #ifndef SMALL 1035 if (Sflag == NULL) { 1036 # ifndef NO_BZIP2_SUPPORT 1037 if (method == FT_BZIP2) 1038 suffix = BZ2_SUFFIX; 1039 else 1040 # endif 1041 # ifndef NO_COMPRESS_SUPPORT 1042 if (method == FT_Z) 1043 suffix = Z_SUFFIX; 1044 # endif 1045 } 1046 1047 if (fflag == 0 && method == FT_UNKNOWN) { 1048 maybe_warnx("%s: not in gzip format", file); 1049 goto lose_close_it; 1050 } 1051 #endif 1052 1053 if (cflag == 0 || lflag) { 1054 s = &file[len - suffix_len + 1]; 1055 if (strncmp(s, suffix, suffix_len) == 0) { 1056 (void)strncpy(outfile, file, len - suffix_len + 1); 1057 outfile[len - suffix_len + 1] = '\0'; 1058 } else if (lflag == 0) { 1059 maybe_warnx("unknown suffix %s", s); 1060 goto lose_close_it; 1061 } 1062 } 1063 1064 #ifdef SMALL 1065 if (method == FT_GZIP && lflag) 1066 #else 1067 if (method == FT_GZIP && (Nflag || lflag)) 1068 #endif 1069 { 1070 #ifndef SMALL 1071 unsigned char header2[4]; /* timestamp */ 1072 1073 if (lseek(fd, GZIP_TIMESTAMP, SEEK_SET) == -1) { 1074 maybe_warn("can't lseek %s", file); 1075 goto close_header_read; 1076 } 1077 if (read(fd, header2, sizeof header2) != sizeof header2) { 1078 if (fflag) 1079 goto lose_close_it; 1080 maybe_warn("can't read %s", file); 1081 goto lose; 1082 } 1083 timestamp = ((time_t)header2[3] << 24) 1084 + ((time_t)header2[2] << 16) 1085 + ((time_t)header2[1] << 8) 1086 + (time_t)header2[0]; 1087 #endif 1088 1089 if (header1[3] & ORIG_NAME) { 1090 size_t rbytes; 1091 int i; 1092 1093 if (lseek(fd, GZIP_ORIGNAME, SEEK_SET) == -1) { 1094 maybe_warn("can't lseek %s", file); 1095 goto close_header_read; 1096 } 1097 rbytes = read(fd, name, PATH_MAX + 1); 1098 if (rbytes < 0) { 1099 maybe_warn("can't read %s", file); 1100 goto lose_close_it; 1101 } 1102 for (i = 0; i < rbytes && name[i]; i++) 1103 ; 1104 if (i < rbytes) { 1105 name[i] = 0; 1106 /* now maybe merge old dirname */ 1107 if (strchr(outfile, '/') == NULL) 1108 (void) strlcpy(outfile, name, outsize); 1109 else { 1110 char newbuf[PATH_MAX + 1]; 1111 1112 (void) snprintf(newbuf, sizeof(newbuf), 1113 "%s/%s", dirname(outfile), 1114 name); 1115 (void) strlcpy(outfile, newbuf, 1116 outsize); 1117 } 1118 } 1119 } 1120 } 1121 close_header_read: 1122 close(fd); 1123 1124 if (cflag == 0 || lflag) { 1125 #ifndef SMALL 1126 if (check_outfile(outfile, &osb) == 0) 1127 goto lose; 1128 1129 #endif 1130 if (stat(file, &isb) == 0) { 1131 #ifndef SMALL 1132 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1133 maybe_warnx("%s has %d other links -- skipping", 1134 file, isb.st_nlink - 1); 1135 goto lose; 1136 } 1137 if (nflag == 0 && timestamp) 1138 isb.st_mtime = timestamp; 1139 #endif 1140 } else 1141 goto lose; 1142 } 1143 1144 if (cflag == 0 && lflag == 0) { 1145 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1146 if (zfd == -1) { 1147 maybe_warn("can't open %s", outfile); 1148 goto lose; 1149 } 1150 } else 1151 zfd = STDOUT_FILENO; 1152 1153 #ifndef NO_BZIP2_SUPPORT 1154 if (method == FT_BZIP2) { 1155 int in; 1156 1157 /* XXX */ 1158 if (lflag) { 1159 maybe_warnx("no -l with bzip2 files"); 1160 goto lose; 1161 } 1162 1163 if ((in = open(file, O_RDONLY)) == -1) { 1164 maybe_warn("open for read: %s", file); 1165 goto lose; 1166 } 1167 1168 size = unbzip2(in, zfd, NULL, 0, NULL); 1169 if (size == -1) { 1170 if (cflag == 0) 1171 unlink(outfile); 1172 maybe_warnx("%s: uncompress failed", file); 1173 goto lose; 1174 } 1175 if (close(in) != 0) 1176 maybe_warn("couldn't close input"); 1177 if (cflag == 0 && close(zfd) != 0) 1178 maybe_warn("couldn't close output"); 1179 } else 1180 #endif 1181 1182 #ifndef NO_COMPRESS_SUPPORT 1183 if (method == FT_Z) { 1184 FILE *in, *out; 1185 1186 /* XXX */ 1187 if (lflag) { 1188 maybe_warnx("no -l with Lempel-Ziv files"); 1189 goto lose; 1190 } 1191 1192 if ((in = zopen(file, NULL)) == NULL) { 1193 maybe_warn("open for read: %s", file); 1194 goto lose; 1195 } 1196 1197 out = fdopen(zfd, "w"); 1198 if (out == NULL) { 1199 maybe_warn("open for write: %s", outfile); 1200 goto lose; 1201 } 1202 1203 size = zuncompress(in, out, NULL, 0, NULL); 1204 if (ferror(in) || fclose(in) != 0) { 1205 unlink(outfile); 1206 (void)fclose(out); 1207 maybe_warn("failed infile fclose"); 1208 } 1209 if (cflag == 0) { 1210 if (size == -1) { 1211 maybe_warnx("%s: uncompress failed", file); 1212 (void)fclose(out); 1213 unlink(outfile); 1214 goto lose; 1215 } 1216 if (fclose(out) != 0) { 1217 unlink(outfile); 1218 maybe_warn("failed outfile close"); 1219 goto lose; 1220 } 1221 } 1222 } else 1223 #endif 1224 { 1225 int in; 1226 1227 if (lflag) { 1228 if ((zfd = open(file, O_RDONLY)) == -1) { 1229 maybe_warn("open: %s", file); 1230 goto lose; 1231 } 1232 print_list(zfd, isb.st_size, outfile, isb.st_mtime); 1233 return 0; /* XXX */ 1234 } 1235 1236 in = open(file, O_RDONLY); 1237 if (in == -1) { 1238 maybe_warn("can't open %s", file); 1239 goto lose; 1240 } 1241 1242 size = gz_uncompress(in, zfd, NULL, 0, NULL, file); 1243 (void)close(in); 1244 if (cflag == 0) { 1245 if (close(zfd)) 1246 maybe_warn("failed close"); 1247 if (size == -1) { 1248 maybe_warnx("%s: uncompress failed", file); 1249 unlink(outfile); 1250 goto lose; 1251 } 1252 } 1253 } 1254 1255 /* if testing, or we uncompressed to stdout, this is all we need */ 1256 #ifndef SMALL 1257 if (tflag) 1258 return (size); 1259 #endif 1260 if (cflag) 1261 return (size); 1262 1263 /* 1264 * if we create a file... 1265 */ 1266 if (cflag == 0) { 1267 /* 1268 * if we can't stat the file, or we are uncompressing to 1269 * stdin, don't remove the file. 1270 */ 1271 if (stat(outfile, &osb) < 0) { 1272 maybe_warn("couldn't stat (leaving original): %s", 1273 outfile); 1274 goto lose; 1275 } 1276 if (osb.st_size != size) { 1277 maybe_warn("stat gave different size: %llu != %llu " 1278 "(leaving original)", 1279 (unsigned long long)size, 1280 (unsigned long long)osb.st_size); 1281 goto lose; 1282 } 1283 if (cflag == 0) 1284 unlink(file); 1285 size = osb.st_size; 1286 #ifndef SMALL 1287 copymodes(outfile, &isb); 1288 #endif 1289 } 1290 return (size); 1291 1292 lose_close_it: 1293 close(fd); 1294 lose: 1295 return -1; 1296 } 1297 1298 #ifndef SMALL 1299 static off_t 1300 cat_stdin(unsigned char * prepend, size_t count, off_t *gsizep) 1301 { 1302 char buf[BUFLEN]; 1303 size_t rv; 1304 off_t in_tot; 1305 1306 in_tot = count; 1307 if (write(STDOUT_FILENO, prepend, count) != count) { 1308 maybe_warn("write to stdout"); 1309 return -1; 1310 } 1311 for (;;) { 1312 rv = read(STDIN_FILENO, buf, sizeof buf); 1313 1314 if (write(STDOUT_FILENO, buf, rv) != rv) { 1315 maybe_warn("write to stdout"); 1316 return -1; 1317 } 1318 in_tot += rv; 1319 } 1320 1321 if (gsizep) 1322 *gsizep = in_tot; 1323 return (in_tot); 1324 } 1325 #endif 1326 1327 static void 1328 handle_stdin(void) 1329 { 1330 unsigned char header1[4]; 1331 off_t usize, gsize; 1332 enum filetype method; 1333 #ifndef NO_COMPRESS_SUPPORT 1334 FILE *in; 1335 #endif 1336 1337 #ifndef SMALL 1338 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1339 maybe_warnx("standard input is a terminal -- ignoring"); 1340 return; 1341 } 1342 #endif 1343 1344 if (lflag) { 1345 struct stat isb; 1346 1347 /* XXX could read the whole file, etc. */ 1348 if (fstat(STDIN_FILENO, &isb) < 0) { 1349 maybe_warn("fstat"); 1350 return; 1351 } 1352 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1353 return; 1354 } 1355 1356 if (read(STDIN_FILENO, header1, sizeof header1) != sizeof header1) { 1357 maybe_warn("can't read stdin"); 1358 return; 1359 } 1360 1361 method = file_gettype(header1); 1362 switch (method) { 1363 default: 1364 #ifndef SMALL 1365 if (fflag == 0) { 1366 maybe_warnx("unknown compression format"); 1367 return; 1368 } 1369 usize = cat_stdin(header1, sizeof header1, &gsize); 1370 break; 1371 #endif 1372 case FT_GZIP: 1373 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1374 header1, sizeof header1, &gsize, "(stdin)"); 1375 break; 1376 #ifndef NO_BZIP2_SUPPORT 1377 case FT_BZIP2: 1378 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1379 header1, sizeof header1, &gsize); 1380 break; 1381 #endif 1382 #ifndef NO_COMPRESS_SUPPORT 1383 case FT_Z: 1384 if ((in = zopen(NULL, stdin)) == NULL) { 1385 maybe_warnx("zopen of stdin"); 1386 return; 1387 } 1388 1389 usize = zuncompress(in, stdout, header1, sizeof header1, &gsize); 1390 break; 1391 #endif 1392 } 1393 1394 #ifndef SMALL 1395 if (vflag && !tflag && usize != -1 && gsize != -1) 1396 print_verbage(NULL, 0, usize, gsize); 1397 #endif 1398 1399 } 1400 1401 static void 1402 handle_stdout(void) 1403 { 1404 off_t gsize, usize; 1405 1406 #ifndef SMALL 1407 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1408 maybe_warnx("standard output is a terminal -- ignoring"); 1409 return; 1410 } 1411 #endif 1412 usize = gz_compress(stdin, STDOUT_FILENO, &gsize, NULL, 0); 1413 1414 #ifndef SMALL 1415 if (vflag && !tflag && usize != -1 && gsize != -1) 1416 print_verbage(NULL, 0, usize, gsize); 1417 #endif 1418 } 1419 1420 /* do what is asked for, for the path name */ 1421 static void 1422 handle_pathname(char *path) 1423 { 1424 char *opath = path, *s = NULL; 1425 ssize_t len; 1426 struct stat sb; 1427 1428 /* check for stdout/stdin */ 1429 if (path[0] == '-' && path[1] == '\0') { 1430 if (dflag) 1431 handle_stdin(); 1432 else 1433 handle_stdout(); 1434 } 1435 1436 retry: 1437 if (stat(path, &sb) < 0) { 1438 /* lets try <path>.gz if we're decompressing */ 1439 if (dflag && s == NULL && errno == ENOENT) { 1440 len = strlen(path); 1441 s = malloc(len + suffix_len + 1); 1442 if (s == NULL) 1443 maybe_err("malloc"); 1444 memmove(s, path, len); 1445 memmove(&s[len], suffix, suffix_len); 1446 s[len + suffix_len] = 0x0; 1447 path = s; 1448 goto retry; 1449 } 1450 maybe_warn("can't stat: %s", opath); 1451 goto out; 1452 } 1453 1454 if (S_ISDIR(sb.st_mode)) { 1455 #ifndef SMALL 1456 if (rflag) 1457 handle_dir(path, &sb); 1458 else 1459 #endif 1460 maybe_warn("%s is a directory", path); 1461 goto out; 1462 } 1463 1464 if (S_ISREG(sb.st_mode)) 1465 handle_file(path, &sb); 1466 1467 out: 1468 if (s) 1469 free(s); 1470 } 1471 1472 /* compress/decompress a file */ 1473 static void 1474 handle_file(char *file, struct stat *sbp) 1475 { 1476 off_t usize, gsize; 1477 char outfile[PATH_MAX]; 1478 1479 infile = file; 1480 if (dflag) { 1481 usize = file_uncompress(file, outfile, sizeof(outfile)); 1482 if (usize == -1) 1483 return; 1484 gsize = sbp->st_size; 1485 } else { 1486 gsize = file_compress(file, outfile, sizeof(outfile)); 1487 if (gsize == -1) 1488 return; 1489 usize = sbp->st_size; 1490 } 1491 1492 1493 #ifndef SMALL 1494 if (vflag && !tflag) 1495 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1496 #endif 1497 } 1498 1499 #ifndef SMALL 1500 /* this is used with -r to recursively decend directories */ 1501 static void 1502 handle_dir(char *dir, struct stat *sbp) 1503 { 1504 char *path_argv[2]; 1505 FTS *fts; 1506 FTSENT *entry; 1507 1508 path_argv[0] = dir; 1509 path_argv[1] = 0; 1510 fts = fts_open(path_argv, FTS_PHYSICAL, NULL); 1511 if (fts == NULL) { 1512 warn("couldn't fts_open %s", dir); 1513 return; 1514 } 1515 1516 while ((entry = fts_read(fts))) { 1517 switch(entry->fts_info) { 1518 case FTS_D: 1519 case FTS_DP: 1520 continue; 1521 1522 case FTS_DNR: 1523 case FTS_ERR: 1524 case FTS_NS: 1525 maybe_warn("%s", entry->fts_path); 1526 continue; 1527 case FTS_F: 1528 handle_file(entry->fts_name, entry->fts_statp); 1529 } 1530 } 1531 (void)fts_close(fts); 1532 } 1533 #endif 1534 1535 /* print a ratio */ 1536 static void 1537 print_ratio(off_t in, off_t out, FILE *where) 1538 { 1539 int64_t percent10; /* 10 * percent */ 1540 off_t diff = in - out; 1541 char ch; 1542 1543 if (in == 0) 1544 percent10 = 0; 1545 else if (diff > 0x400000) /* anything with 22 or more bits */ 1546 percent10 = diff / (in / 1000); 1547 else 1548 percent10 = (1000 * diff) / in; 1549 1550 if (percent10 < 0) { 1551 percent10 = -percent10; 1552 ch = '-'; 1553 } else 1554 ch = ' '; 1555 1556 /* 1557 * ugh. for negative percentages < 10, we need to avoid printing a 1558 * a space between the "-" and the single number. 1559 */ 1560 if (ch == '-' && percent10 / 10LL < 10) 1561 fprintf(where, " -%1d.%1u%%", (unsigned)(percent10 / 10LL), 1562 (unsigned)(percent10 % 10LL)); 1563 else 1564 fprintf(where, "%c%2d.%1u%%", ch, (unsigned)(percent10 / 10LL), 1565 (unsigned)(percent10 % 10LL)); 1566 } 1567 1568 #ifndef SMALL 1569 /* print compression statistics, and the new name (if there is one!) */ 1570 static void 1571 print_verbage(char *file, char *nfile, off_t usize, off_t gsize) 1572 { 1573 if (file) 1574 fprintf(stderr, "%s:%s ", file, 1575 strlen(file) < 7 ? "\t\t" : "\t"); 1576 print_ratio((off_t)usize, (off_t)gsize, stderr); 1577 if (nfile) 1578 fprintf(stderr, " -- replaced with %s", nfile); 1579 fprintf(stderr, "\n"); 1580 fflush(stderr); 1581 } 1582 1583 /* print test results */ 1584 static void 1585 print_test(const char *file, int ok) 1586 { 1587 1588 if (exit_value == 0 && ok == 0) 1589 exit_value = 1; 1590 fprintf(stderr, "%s:%s %s\n", file, 1591 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1592 fflush(stderr); 1593 } 1594 #endif 1595 1596 /* print a file's info ala --list */ 1597 /* eg: 1598 compressed uncompressed ratio uncompressed_name 1599 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1600 */ 1601 static void 1602 print_list(int fd, off_t out, const char *outfile, time_t ts) 1603 { 1604 static int first = 1; 1605 #ifndef SMALL 1606 static off_t in_tot, out_tot; 1607 u_int32_t crc; 1608 #endif 1609 off_t in; 1610 int rv; 1611 1612 if (first) { 1613 #ifndef SMALL 1614 if (vflag) 1615 printf("method crc date time "); 1616 #endif 1617 if (qflag == 0) 1618 printf(" compressed uncompressed " 1619 "ratio uncompressed_name\n"); 1620 } 1621 first = 0; 1622 1623 /* print totals? */ 1624 #ifndef SMALL 1625 if (fd == -1) { 1626 in = in_tot; 1627 out = out_tot; 1628 } else 1629 #endif 1630 { 1631 /* read the last 4 bytes - this is the uncompressed size */ 1632 rv = lseek(fd, (off_t)(-8), SEEK_END); 1633 if (rv != -1) { 1634 unsigned char buf[8]; 1635 u_int32_t usize; 1636 1637 if (read(fd, (char *)buf, sizeof(buf)) != sizeof(buf)) 1638 maybe_warn("read of uncompressed size"); 1639 usize = buf[4] | buf[5] << 8 | buf[6] << 16 | buf[7] << 24; 1640 in = (off_t)usize; 1641 #ifndef SMALL 1642 crc = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; 1643 #endif 1644 } 1645 } 1646 1647 #ifndef SMALL 1648 if (vflag && fd == -1) 1649 printf(" "); 1650 else if (vflag) { 1651 char *date = ctime(&ts); 1652 1653 /* skip the day, 1/100th second, and year */ 1654 date += 4; 1655 date[12] = 0; 1656 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 1657 } 1658 in_tot += in; 1659 out_tot += out; 1660 #endif 1661 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 1662 print_ratio(in, out, stdout); 1663 printf(" %s\n", outfile); 1664 } 1665 1666 /* display the usage of NetBSD gzip */ 1667 static void 1668 usage(void) 1669 { 1670 1671 fprintf(stderr, "%s\n", gzip_version); 1672 fprintf(stderr, 1673 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n" 1674 #ifndef SMALL 1675 " -c --stdout write to stdout, keep original files\n" 1676 " --to-stdout\n" 1677 " -d --decompress uncompress files\n" 1678 " --uncompress\n" 1679 " -f --force force overwriting & compress links\n" 1680 " -h --help display this help\n" 1681 " -n --no-name don't save original file name or time stamp\n" 1682 " -N --name save or restore original file name and time stamp\n" 1683 " -q --quiet output no warnings\n" 1684 " -r --recursive recursively compress files in directories\n" 1685 " -S .suf use suffix .suf instead of .gz\n" 1686 " --suffix .suf\n" 1687 " -t --test test compressed file\n" 1688 " -v --verbose print extra statistics\n" 1689 " -V --version display program version\n" 1690 " -1 --fast fastest (worst) compression\n" 1691 " -2 .. -8 set compression level\n" 1692 " -9 --best best (slowest) compression\n", 1693 #else 1694 , 1695 #endif 1696 getprogname()); 1697 exit(0); 1698 } 1699 1700 /* display the version of NetBSD gzip */ 1701 static void 1702 display_version(void) 1703 { 1704 1705 fprintf(stderr, "%s\n", gzip_version); 1706 exit(0); 1707 } 1708 1709 #ifndef NO_BZIP2_SUPPORT 1710 #include "unbzip2.c" 1711 #endif 1712 #ifndef NO_COMPRESS_SUPPORT 1713 #include "zuncompress.c" 1714 #endif 1715