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