1 /* $NetBSD: gzip.c,v 1.50 2004/06/22 12:01:29 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.50 2004/06/22 12:01:29 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 /* Z_BUF_ERROR goes with Z_FINISH... */ 753 if (error == Z_STREAM_END || error == Z_BUF_ERROR) { 754 size_t wr = BUFLEN - z.avail_out; 755 756 /* Nothing left? */ 757 if (wr == 0) 758 goto stop; 759 760 if ( 761 #ifndef SMALL 762 /* don't write anything with -t */ 763 tflag == 0 && 764 #endif 765 write(out, outbuf, wr) != wr) { 766 maybe_warn("error writing " 767 "to output\n"); 768 out_tot = -1; 769 goto stop; 770 } 771 772 out_tot += wr; 773 774 if (error == Z_STREAM_END) 775 goto stop; 776 777 z.next_out = outbuf; 778 z.avail_out = BUFLEN; 779 780 break; 781 } 782 if (error < 0) { 783 maybe_warnx("decompression error\n"); 784 out_tot = -1; 785 goto stop; 786 } 787 break; 788 } 789 continue; 790 stop: 791 break; 792 } 793 if (state > GZSTATE_INIT) 794 inflateEnd(&z); 795 796 #ifndef SMALL 797 if (tflag && vflag) 798 print_test(filename, out_tot != -1); 799 #endif 800 801 if (gsizep) 802 *gsizep = in_tot; 803 return (out_tot); 804 } 805 806 #ifndef SMALL 807 /* 808 * set the owner, mode, flags & utimes for a file 809 */ 810 static void 811 copymodes(const char *file, struct stat *sbp) 812 { 813 struct timeval times[2]; 814 815 /* 816 * If we have no info on the input, give this file some 817 * default values and return.. 818 */ 819 if (sbp == NULL) { 820 mode_t mask = umask(022); 821 822 (void)chmod(file, DEFFILEMODE & ~mask); 823 (void)umask(mask); 824 return; 825 } 826 827 /* if the chown fails, remove set-id bits as-per compress(1) */ 828 if (chown(file, sbp->st_uid, sbp->st_gid) < 0) { 829 if (errno != EPERM) 830 maybe_warn("couldn't chown: %s", file); 831 sbp->st_mode &= ~(S_ISUID|S_ISGID); 832 } 833 834 /* we only allow set-id and the 9 normal permission bits */ 835 sbp->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 836 if (chmod(file, sbp->st_mode) < 0) 837 maybe_warn("couldn't chmod: %s", file); 838 839 /* only try flags if they exist already */ 840 if (sbp->st_flags != 0 && chflags(file, sbp->st_flags) < 0) 841 maybe_warn("couldn't chflags: %s", file); 842 843 TIMESPEC_TO_TIMEVAL(×[0], &sbp->st_atimespec); 844 TIMESPEC_TO_TIMEVAL(×[1], &sbp->st_mtimespec); 845 if (utimes(file, times) < 0) 846 maybe_warn("couldn't utimes: %s", file); 847 } 848 #endif 849 850 /* what sort of file is this? */ 851 static enum filetype 852 file_gettype(u_char *buf) 853 { 854 855 if (buf[0] == GZIP_MAGIC0 && 856 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 857 return FT_GZIP; 858 else 859 #ifndef NO_BZIP2_SUPPORT 860 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 861 buf[3] >= '0' && buf[3] <= '9') 862 return FT_BZIP2; 863 else 864 #endif 865 #ifndef NO_COMPRESS_SUPPORT 866 if (memcmp(buf, Z_MAGIC, 2) == 0) 867 return FT_Z; 868 else 869 #endif 870 return FT_UNKNOWN; 871 } 872 873 #ifndef SMALL 874 /* check the outfile is OK. */ 875 static int 876 check_outfile(const char *outfile, struct stat *sb) 877 { 878 int ok = 1; 879 880 if (fflag == 0 && lflag == 0 && stat(outfile, sb) == 0) { 881 if (isatty(STDIN_FILENO)) { 882 char ans[10] = { 'n', '\0' }; /* default */ 883 884 fprintf(stderr, "%s already exists -- do you wish to " 885 "overwrite (y or n)? " , outfile); 886 (void)fgets(ans, sizeof(ans) - 1, stdin); 887 if (ans[0] != 'y' && ans[0] != 'Y') { 888 fprintf(stderr, "\tnot overwritting\n"); 889 ok = 0; 890 } else 891 unlink(outfile); 892 } else { 893 maybe_warnx("%s already exists -- skipping", outfile); 894 ok = 0; 895 } 896 } 897 return ok; 898 } 899 #endif 900 901 /* 902 * compress the given file: create a corresponding .gz file and remove the 903 * original. 904 */ 905 static off_t 906 file_compress(char *file, char *outfile, size_t outsize) 907 { 908 FILE *in; 909 int out; 910 struct stat isb, osb; 911 off_t size; 912 #ifndef SMALL 913 u_int32_t mtime = 0; 914 char *savename; 915 #endif 916 917 if (cflag == 0) { 918 (void)strncpy(outfile, file, outsize - suffix_len); 919 outfile[outsize - suffix_len] = '\0'; 920 (void)strlcat(outfile, suffix, outsize); 921 922 #ifndef SMALL 923 if (check_outfile(outfile, &osb) == 0) 924 goto lose; 925 926 if (stat(file, &isb) == 0) { 927 if (isb.st_nlink > 1 && fflag == 0) { 928 maybe_warnx("%s has %d other link%s -- " 929 "skipping", file, isb.st_nlink - 1, 930 isb.st_nlink == 1 ? "" : "s"); 931 goto lose; 932 } 933 if (nflag == 0) 934 mtime = (u_int32_t)isb.st_mtime; 935 } 936 #endif 937 } 938 in = fopen(file, "r"); 939 if (in == NULL) { 940 maybe_warn("can't fopen %s", file); 941 goto lose; 942 } 943 944 #ifndef SMALL 945 if (nflag == 0) 946 savename = basename(file); 947 else 948 savename = NULL; 949 #endif 950 if (cflag == 0) { 951 out = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 952 if (out == -1) { 953 maybe_warn("could not create output: %s", outfile); 954 goto lose; 955 } 956 } else 957 out = STDOUT_FILENO; 958 959 #ifdef SMALL 960 gz_compress(in, out, NULL, NULL, 0); 961 #else 962 gz_compress(in, out, NULL, savename, mtime); 963 #endif 964 965 (void)fclose(in); 966 967 /* 968 * if we compressed to stdout, we don't know the size and 969 * we don't know the new file name, punt. if we can't stat 970 * the file, whine, otherwise set the size from the stat 971 * buffer. we only blow away the file if we can stat the 972 * output, just in case. 973 */ 974 if (cflag == 0) { 975 if (close(out) == -1) 976 maybe_warn("couldn't close ouput"); 977 978 if (stat(outfile, &osb) < 0) { 979 maybe_warn("couldn't stat: %s", outfile); 980 maybe_warnx("leaving original %s", file); 981 size = 0; 982 } else { 983 unlink(file); 984 size = osb.st_size; 985 } 986 #ifndef SMALL 987 copymodes(outfile, &isb); 988 #endif 989 } else { 990 lose: 991 size = -1; 992 } 993 994 return (size); 995 } 996 997 /* uncompress the given file and remove the original */ 998 static off_t 999 file_uncompress(char *file, char *outfile, size_t outsize) 1000 { 1001 struct stat isb, osb; 1002 char *s; 1003 off_t size; 1004 ssize_t len = strlen(file); 1005 unsigned char header1[4], name[PATH_MAX + 1]; 1006 enum filetype method; 1007 int fd, zfd; 1008 #ifndef SMALL 1009 time_t timestamp = 0; 1010 #endif 1011 1012 /* gather the old name info */ 1013 1014 fd = open(file, O_RDONLY); 1015 if (fd < 0) { 1016 maybe_warn("can't open %s", file); 1017 goto lose; 1018 } 1019 if (read(fd, header1, sizeof header1) != sizeof header1) { 1020 /* we don't want to fail here. */ 1021 #ifndef SMALL 1022 if (fflag) 1023 goto lose_close_it; 1024 #endif 1025 maybe_warn("can't read %s", file); 1026 goto lose; 1027 } 1028 1029 method = file_gettype(header1); 1030 1031 #ifndef SMALL 1032 if (Sflag == NULL) { 1033 # ifndef NO_BZIP2_SUPPORT 1034 if (method == FT_BZIP2) 1035 suffix = BZ2_SUFFIX; 1036 else 1037 # endif 1038 # ifndef NO_COMPRESS_SUPPORT 1039 if (method == FT_Z) 1040 suffix = Z_SUFFIX; 1041 # endif 1042 } 1043 1044 if (fflag == 0 && method == FT_UNKNOWN) { 1045 maybe_warnx("%s: not in gzip format", file); 1046 goto lose_close_it; 1047 } 1048 #endif 1049 1050 if (cflag == 0 || lflag) { 1051 s = &file[len - suffix_len + 1]; 1052 if (strncmp(s, suffix, suffix_len) == 0) { 1053 (void)strncpy(outfile, file, len - suffix_len + 1); 1054 outfile[len - suffix_len + 1] = '\0'; 1055 } else if (lflag == 0) { 1056 maybe_warnx("unknown suffix %s", s); 1057 goto lose_close_it; 1058 } 1059 } 1060 1061 #ifdef SMALL 1062 if (method == FT_GZIP && lflag) 1063 #else 1064 if (method == FT_GZIP && (Nflag || lflag)) 1065 #endif 1066 { 1067 #ifndef SMALL 1068 unsigned char header2[4]; /* timestamp */ 1069 1070 if (lseek(fd, GZIP_TIMESTAMP, SEEK_SET) == -1) { 1071 maybe_warn("can't lseek %s", file); 1072 goto close_header_read; 1073 } 1074 if (read(fd, header2, sizeof header2) != sizeof header2) { 1075 if (fflag) 1076 goto lose_close_it; 1077 maybe_warn("can't read %s", file); 1078 goto lose; 1079 } 1080 timestamp = ((time_t)header2[3] << 24) 1081 + ((time_t)header2[2] << 16) 1082 + ((time_t)header2[1] << 8) 1083 + (time_t)header2[0]; 1084 #endif 1085 1086 if (header1[3] & ORIG_NAME) { 1087 size_t rbytes; 1088 int i; 1089 1090 if (lseek(fd, GZIP_ORIGNAME, SEEK_SET) == -1) { 1091 maybe_warn("can't lseek %s", file); 1092 goto close_header_read; 1093 } 1094 rbytes = read(fd, name, PATH_MAX + 1); 1095 if (rbytes < 0) { 1096 maybe_warn("can't read %s", file); 1097 goto lose_close_it; 1098 } 1099 for (i = 0; i < rbytes && name[i]; i++) 1100 ; 1101 if (i < rbytes) { 1102 name[i] = 0; 1103 /* now maybe merge old dirname */ 1104 if (strchr(outfile, '/') == NULL) 1105 (void) strlcpy(outfile, name, outsize); 1106 else { 1107 char newbuf[PATH_MAX + 1]; 1108 1109 (void) snprintf(newbuf, sizeof(newbuf), 1110 "%s/%s", dirname(outfile), 1111 name); 1112 (void) strlcpy(outfile, newbuf, 1113 outsize); 1114 } 1115 } 1116 } 1117 } 1118 close_header_read: 1119 close(fd); 1120 1121 if (cflag == 0 || lflag) { 1122 #ifndef SMALL 1123 if (check_outfile(outfile, &osb) == 0) 1124 goto lose; 1125 1126 #endif 1127 if (stat(file, &isb) == 0) { 1128 #ifndef SMALL 1129 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1130 maybe_warnx("%s has %d other links -- skipping", 1131 file, isb.st_nlink - 1); 1132 goto lose; 1133 } 1134 if (nflag == 0 && timestamp) 1135 isb.st_mtime = timestamp; 1136 #endif 1137 } else 1138 goto lose; 1139 } 1140 1141 if (cflag == 0 && lflag == 0) { 1142 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1143 if (zfd == -1) { 1144 maybe_warn("can't open %s", outfile); 1145 goto lose; 1146 } 1147 } else 1148 zfd = STDOUT_FILENO; 1149 1150 #ifndef NO_BZIP2_SUPPORT 1151 if (method == FT_BZIP2) { 1152 int in; 1153 1154 /* XXX */ 1155 if (lflag) { 1156 maybe_warnx("no -l with bzip2 files"); 1157 goto lose; 1158 } 1159 1160 if ((in = open(file, O_RDONLY)) == -1) { 1161 maybe_warn("open for read: %s", file); 1162 goto lose; 1163 } 1164 1165 size = unbzip2(in, zfd, NULL, 0, NULL); 1166 if (size == -1) { 1167 if (cflag == 0) 1168 unlink(outfile); 1169 maybe_warnx("%s: uncompress failed", file); 1170 goto lose; 1171 } 1172 if (close(in) != 0) 1173 maybe_warn("couldn't close input"); 1174 if (cflag == 0 && close(zfd) != 0) 1175 maybe_warn("couldn't close output"); 1176 } else 1177 #endif 1178 1179 #ifndef NO_COMPRESS_SUPPORT 1180 if (method == FT_Z) { 1181 FILE *in, *out; 1182 1183 /* XXX */ 1184 if (lflag) { 1185 maybe_warnx("no -l with Lempel-Ziv files"); 1186 goto lose; 1187 } 1188 1189 if ((in = zopen(file, NULL)) == NULL) { 1190 maybe_warn("open for read: %s", file); 1191 goto lose; 1192 } 1193 1194 out = fdopen(zfd, "w"); 1195 if (out == NULL) { 1196 maybe_warn("open for write: %s", outfile); 1197 goto lose; 1198 } 1199 1200 size = zuncompress(in, out, NULL, 0, NULL); 1201 if (ferror(in) || fclose(in) != 0) { 1202 unlink(outfile); 1203 (void)fclose(out); 1204 maybe_warn("failed infile fclose"); 1205 } 1206 if (cflag == 0) { 1207 if (size == -1) { 1208 maybe_warnx("%s: uncompress failed", file); 1209 (void)fclose(out); 1210 unlink(outfile); 1211 goto lose; 1212 } 1213 if (fclose(out) != 0) { 1214 unlink(outfile); 1215 maybe_warn("failed outfile close"); 1216 goto lose; 1217 } 1218 } 1219 } else 1220 #endif 1221 { 1222 int in; 1223 1224 if (lflag) { 1225 if ((zfd = open(file, O_RDONLY)) == -1) { 1226 maybe_warn("open: %s", file); 1227 goto lose; 1228 } 1229 print_list(zfd, isb.st_size, outfile, isb.st_mtime); 1230 return 0; /* XXX */ 1231 } 1232 1233 in = open(file, O_RDONLY); 1234 if (in == -1) { 1235 maybe_warn("can't open %s", file); 1236 goto lose; 1237 } 1238 1239 size = gz_uncompress(in, zfd, NULL, 0, NULL, file); 1240 (void)close(in); 1241 if (cflag == 0) { 1242 if (close(zfd)) 1243 maybe_warn("failed close"); 1244 if (size == -1) { 1245 maybe_warnx("%s: uncompress failed", file); 1246 unlink(outfile); 1247 goto lose; 1248 } 1249 } 1250 } 1251 1252 /* if testing, or we uncompressed to stdout, this is all we need */ 1253 #ifndef SMALL 1254 if (tflag) 1255 return (size); 1256 #endif 1257 if (cflag) 1258 return (size); 1259 1260 /* 1261 * if we create a file... 1262 */ 1263 if (cflag == 0) { 1264 /* 1265 * if we can't stat the file, or we are uncompressing to 1266 * stdin, don't remove the file. 1267 */ 1268 if (stat(outfile, &osb) < 0) { 1269 maybe_warn("couldn't stat (leaving original): %s", 1270 outfile); 1271 goto lose; 1272 } 1273 if (osb.st_size != size) { 1274 maybe_warn("stat gave different size: %llu != %llu " 1275 "(leaving original)", 1276 (unsigned long long)size, 1277 (unsigned long long)osb.st_size); 1278 goto lose; 1279 } 1280 if (cflag == 0) 1281 unlink(file); 1282 size = osb.st_size; 1283 #ifndef SMALL 1284 copymodes(outfile, &isb); 1285 #endif 1286 } 1287 return (size); 1288 1289 lose_close_it: 1290 close(fd); 1291 lose: 1292 return -1; 1293 } 1294 1295 #ifndef SMALL 1296 static off_t 1297 cat_stdin(unsigned char * prepend, size_t count, off_t *gsizep) 1298 { 1299 char buf[BUFLEN]; 1300 size_t rv; 1301 off_t in_tot; 1302 1303 in_tot = count; 1304 if (write(STDOUT_FILENO, prepend, count) != count) { 1305 maybe_warn("write to stdout"); 1306 return -1; 1307 } 1308 for (;;) { 1309 rv = read(STDIN_FILENO, buf, sizeof buf); 1310 1311 if (write(STDOUT_FILENO, buf, rv) != rv) { 1312 maybe_warn("write to stdout"); 1313 return -1; 1314 } 1315 in_tot += rv; 1316 } 1317 1318 if (gsizep) 1319 *gsizep = in_tot; 1320 return (in_tot); 1321 } 1322 #endif 1323 1324 static void 1325 handle_stdin(void) 1326 { 1327 unsigned char header1[4]; 1328 off_t usize, gsize; 1329 enum filetype method; 1330 #ifndef NO_COMPRESS_SUPPORT 1331 FILE *in; 1332 #endif 1333 1334 #ifndef SMALL 1335 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1336 maybe_warnx("standard input is a terminal -- ignoring"); 1337 return; 1338 } 1339 #endif 1340 1341 if (lflag) { 1342 struct stat isb; 1343 1344 /* XXX could read the whole file, etc. */ 1345 if (fstat(STDIN_FILENO, &isb) < 0) { 1346 maybe_warn("fstat"); 1347 return; 1348 } 1349 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1350 return; 1351 } 1352 1353 if (read(STDIN_FILENO, header1, sizeof header1) != sizeof header1) { 1354 maybe_warn("can't read stdin"); 1355 return; 1356 } 1357 1358 method = file_gettype(header1); 1359 switch (method) { 1360 default: 1361 #ifndef SMALL 1362 if (fflag == 0) { 1363 maybe_warnx("unknown compression format"); 1364 return; 1365 } 1366 usize = cat_stdin(header1, sizeof header1, &gsize); 1367 break; 1368 #endif 1369 case FT_GZIP: 1370 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1371 header1, sizeof header1, &gsize, "(stdin)"); 1372 break; 1373 #ifndef NO_BZIP2_SUPPORT 1374 case FT_BZIP2: 1375 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1376 header1, sizeof header1, &gsize); 1377 break; 1378 #endif 1379 #ifndef NO_COMPRESS_SUPPORT 1380 case FT_Z: 1381 if ((in = zopen(NULL, stdin)) == NULL) { 1382 maybe_warnx("zopen of stdin"); 1383 return; 1384 } 1385 1386 usize = zuncompress(in, stdout, header1, sizeof header1, &gsize); 1387 break; 1388 #endif 1389 } 1390 1391 #ifndef SMALL 1392 if (vflag && !tflag && usize != -1 && gsize != -1) 1393 print_verbage(NULL, 0, usize, gsize); 1394 #endif 1395 1396 } 1397 1398 static void 1399 handle_stdout(void) 1400 { 1401 off_t gsize, usize; 1402 1403 #ifndef SMALL 1404 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1405 maybe_warnx("standard output is a terminal -- ignoring"); 1406 return; 1407 } 1408 #endif 1409 usize = gz_compress(stdin, STDOUT_FILENO, &gsize, NULL, 0); 1410 1411 #ifndef SMALL 1412 if (vflag && !tflag && usize != -1 && gsize != -1) 1413 print_verbage(NULL, 0, usize, gsize); 1414 #endif 1415 } 1416 1417 /* do what is asked for, for the path name */ 1418 static void 1419 handle_pathname(char *path) 1420 { 1421 char *opath = path, *s = NULL; 1422 ssize_t len; 1423 struct stat sb; 1424 1425 /* check for stdout/stdin */ 1426 if (path[0] == '-' && path[1] == '\0') { 1427 if (dflag) 1428 handle_stdin(); 1429 else 1430 handle_stdout(); 1431 return; 1432 } 1433 1434 retry: 1435 if (stat(path, &sb) < 0) { 1436 /* lets try <path>.gz if we're decompressing */ 1437 if (dflag && s == NULL && errno == ENOENT) { 1438 len = strlen(path); 1439 s = malloc(len + suffix_len + 1); 1440 if (s == NULL) 1441 maybe_err("malloc"); 1442 memmove(s, path, len); 1443 memmove(&s[len], suffix, suffix_len); 1444 s[len + suffix_len] = 0x0; 1445 path = s; 1446 goto retry; 1447 } 1448 maybe_warn("can't stat: %s", opath); 1449 goto out; 1450 } 1451 1452 if (S_ISDIR(sb.st_mode)) { 1453 #ifndef SMALL 1454 if (rflag) 1455 handle_dir(path, &sb); 1456 else 1457 #endif 1458 maybe_warn("%s is a directory", path); 1459 goto out; 1460 } 1461 1462 if (S_ISREG(sb.st_mode)) 1463 handle_file(path, &sb); 1464 1465 out: 1466 if (s) 1467 free(s); 1468 } 1469 1470 /* compress/decompress a file */ 1471 static void 1472 handle_file(char *file, struct stat *sbp) 1473 { 1474 off_t usize, gsize; 1475 char outfile[PATH_MAX]; 1476 1477 infile = file; 1478 if (dflag) { 1479 usize = file_uncompress(file, outfile, sizeof(outfile)); 1480 if (usize == -1) 1481 return; 1482 gsize = sbp->st_size; 1483 } else { 1484 gsize = file_compress(file, outfile, sizeof(outfile)); 1485 if (gsize == -1) 1486 return; 1487 usize = sbp->st_size; 1488 } 1489 1490 1491 #ifndef SMALL 1492 if (vflag && !tflag) 1493 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1494 #endif 1495 } 1496 1497 #ifndef SMALL 1498 /* this is used with -r to recursively decend directories */ 1499 static void 1500 handle_dir(char *dir, struct stat *sbp) 1501 { 1502 char *path_argv[2]; 1503 FTS *fts; 1504 FTSENT *entry; 1505 1506 path_argv[0] = dir; 1507 path_argv[1] = 0; 1508 fts = fts_open(path_argv, FTS_PHYSICAL, NULL); 1509 if (fts == NULL) { 1510 warn("couldn't fts_open %s", dir); 1511 return; 1512 } 1513 1514 while ((entry = fts_read(fts))) { 1515 switch(entry->fts_info) { 1516 case FTS_D: 1517 case FTS_DP: 1518 continue; 1519 1520 case FTS_DNR: 1521 case FTS_ERR: 1522 case FTS_NS: 1523 maybe_warn("%s", entry->fts_path); 1524 continue; 1525 case FTS_F: 1526 handle_file(entry->fts_name, entry->fts_statp); 1527 } 1528 } 1529 (void)fts_close(fts); 1530 } 1531 #endif 1532 1533 /* print a ratio */ 1534 static void 1535 print_ratio(off_t in, off_t out, FILE *where) 1536 { 1537 int64_t percent10; /* 10 * percent */ 1538 off_t diff = in - out; 1539 char ch; 1540 1541 if (in == 0) 1542 percent10 = 0; 1543 else if (diff > 0x400000) /* anything with 22 or more bits */ 1544 percent10 = diff / (in / 1000); 1545 else 1546 percent10 = (1000 * diff) / in; 1547 1548 if (percent10 < 0) { 1549 percent10 = -percent10; 1550 ch = '-'; 1551 } else 1552 ch = ' '; 1553 1554 /* 1555 * ugh. for negative percentages < 10, we need to avoid printing a 1556 * a space between the "-" and the single number. 1557 */ 1558 if (ch == '-' && percent10 / 10LL < 10) 1559 fprintf(where, " -%1d.%1u%%", (unsigned)(percent10 / 10LL), 1560 (unsigned)(percent10 % 10LL)); 1561 else 1562 fprintf(where, "%c%2d.%1u%%", ch, (unsigned)(percent10 / 10LL), 1563 (unsigned)(percent10 % 10LL)); 1564 } 1565 1566 #ifndef SMALL 1567 /* print compression statistics, and the new name (if there is one!) */ 1568 static void 1569 print_verbage(char *file, char *nfile, off_t usize, off_t gsize) 1570 { 1571 if (file) 1572 fprintf(stderr, "%s:%s ", file, 1573 strlen(file) < 7 ? "\t\t" : "\t"); 1574 print_ratio((off_t)usize, (off_t)gsize, stderr); 1575 if (nfile) 1576 fprintf(stderr, " -- replaced with %s", nfile); 1577 fprintf(stderr, "\n"); 1578 fflush(stderr); 1579 } 1580 1581 /* print test results */ 1582 static void 1583 print_test(const char *file, int ok) 1584 { 1585 1586 if (exit_value == 0 && ok == 0) 1587 exit_value = 1; 1588 fprintf(stderr, "%s:%s %s\n", file, 1589 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1590 fflush(stderr); 1591 } 1592 #endif 1593 1594 /* print a file's info ala --list */ 1595 /* eg: 1596 compressed uncompressed ratio uncompressed_name 1597 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1598 */ 1599 static void 1600 print_list(int fd, off_t out, const char *outfile, time_t ts) 1601 { 1602 static int first = 1; 1603 #ifndef SMALL 1604 static off_t in_tot, out_tot; 1605 u_int32_t crc; 1606 #endif 1607 off_t in; 1608 int rv; 1609 1610 if (first) { 1611 #ifndef SMALL 1612 if (vflag) 1613 printf("method crc date time "); 1614 #endif 1615 if (qflag == 0) 1616 printf(" compressed uncompressed " 1617 "ratio uncompressed_name\n"); 1618 } 1619 first = 0; 1620 1621 /* print totals? */ 1622 #ifndef SMALL 1623 if (fd == -1) { 1624 in = in_tot; 1625 out = out_tot; 1626 } else 1627 #endif 1628 { 1629 /* read the last 4 bytes - this is the uncompressed size */ 1630 rv = lseek(fd, (off_t)(-8), SEEK_END); 1631 if (rv != -1) { 1632 unsigned char buf[8]; 1633 u_int32_t usize; 1634 1635 if (read(fd, (char *)buf, sizeof(buf)) != sizeof(buf)) 1636 maybe_warn("read of uncompressed size"); 1637 usize = buf[4] | buf[5] << 8 | buf[6] << 16 | buf[7] << 24; 1638 in = (off_t)usize; 1639 #ifndef SMALL 1640 crc = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; 1641 #endif 1642 } 1643 } 1644 1645 #ifndef SMALL 1646 if (vflag && fd == -1) 1647 printf(" "); 1648 else if (vflag) { 1649 char *date = ctime(&ts); 1650 1651 /* skip the day, 1/100th second, and year */ 1652 date += 4; 1653 date[12] = 0; 1654 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 1655 } 1656 in_tot += in; 1657 out_tot += out; 1658 #endif 1659 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 1660 print_ratio(in, out, stdout); 1661 printf(" %s\n", outfile); 1662 } 1663 1664 /* display the usage of NetBSD gzip */ 1665 static void 1666 usage(void) 1667 { 1668 1669 fprintf(stderr, "%s\n", gzip_version); 1670 fprintf(stderr, 1671 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n" 1672 #ifndef SMALL 1673 " -c --stdout write to stdout, keep original files\n" 1674 " --to-stdout\n" 1675 " -d --decompress uncompress files\n" 1676 " --uncompress\n" 1677 " -f --force force overwriting & compress links\n" 1678 " -h --help display this help\n" 1679 " -n --no-name don't save original file name or time stamp\n" 1680 " -N --name save or restore original file name and time stamp\n" 1681 " -q --quiet output no warnings\n" 1682 " -r --recursive recursively compress files in directories\n" 1683 " -S .suf use suffix .suf instead of .gz\n" 1684 " --suffix .suf\n" 1685 " -t --test test compressed file\n" 1686 " -v --verbose print extra statistics\n" 1687 " -V --version display program version\n" 1688 " -1 --fast fastest (worst) compression\n" 1689 " -2 .. -8 set compression level\n" 1690 " -9 --best best (slowest) compression\n", 1691 #else 1692 , 1693 #endif 1694 getprogname()); 1695 exit(0); 1696 } 1697 1698 /* display the version of NetBSD gzip */ 1699 static void 1700 display_version(void) 1701 { 1702 1703 fprintf(stderr, "%s\n", gzip_version); 1704 exit(0); 1705 } 1706 1707 #ifndef NO_BZIP2_SUPPORT 1708 #include "unbzip2.c" 1709 #endif 1710 #ifndef NO_COMPRESS_SUPPORT 1711 #include "zuncompress.c" 1712 #endif 1713