1 /* $OpenBSD: main.c,v 1.43 2003/07/29 18:33:11 millert Exp $ */ 2 3 static const char copyright[] = 4 "@(#) Copyright (c) 1992, 1993\n\ 5 The Regents of the University of California. All rights reserved.\n" 6 "Copyright (c) 1997-2002 Michael Shalayeff\n"; 7 8 static const char license[] = 9 "\n" 10 " Redistribution and use in source and binary forms, with or without\n" 11 " modification, are permitted provided that the following conditions\n" 12 " are met:\n" 13 " 1. Redistributions of source code must retain the above copyright\n" 14 " notice, this list of conditions and the following disclaimer.\n" 15 " 2. Redistributions in binary form must reproduce the above copyright\n" 16 " notice, this list of conditions and the following disclaimer in the\n" 17 " documentation and/or other materials provided with the distribution.\n" 18 " 3. Neither the name of the University nor the names of its contributors\n" 19 " may be used to endorse or promote products derived from this software\n" 20 " without specific prior written permission.\n" 21 "\n" 22 " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 23 " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 24 " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 25 " IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,\n" 26 " INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n" 27 " (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n" 28 " SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n" 29 " HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n" 30 " STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n" 31 " IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n" 32 " THE POSSIBILITY OF SUCH DAMAGE.\n"; 33 34 #ifndef lint 35 #if 0 36 static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; 37 #else 38 static const char main_rcsid[] = "$OpenBSD: main.c,v 1.43 2003/07/29 18:33:11 millert Exp $"; 39 #endif 40 #endif /* not lint */ 41 42 #include <sys/param.h> 43 #include <sys/time.h> 44 #include <sys/stat.h> 45 46 #include <getopt.h> 47 #include <err.h> 48 #include <errno.h> 49 #include <fts.h> 50 #include <libgen.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <fcntl.h> 56 #include <paths.h> 57 #include "compress.h" 58 59 #define min(a,b) ((a) < (b)? (a) : (b)) 60 61 int pipin, force, verbose, testmode, list, nosave; 62 int savename, recurse; 63 int cat, decomp; 64 extern char *__progname; 65 66 const struct compressor { 67 char *name; 68 char *suffix; 69 u_char *magic; 70 void *(*open)(int, const char *, char *, int, u_int32_t, int); 71 int (*read)(void *, char *, int); 72 int (*write)(void *, const char *, int); 73 int (*close)(void *, struct z_info *); 74 } c_table[] = { 75 #define M_COMPRESS (&c_table[0]) 76 { "compress", ".Z", "\037\235", z_open, zread, zwrite, z_close }, 77 #define M_DEFLATE (&c_table[1]) 78 { "deflate", ".gz", "\037\213", gz_open, gz_read, gz_write, gz_close }, 79 #if 0 80 #define M_LZH (&c_table[2]) 81 { "lzh", ".lzh", "\037\240", lzh_open, lzh_read, lzh_write, lzh_close }, 82 #define M_ZIP (&c_table[3]) 83 { "zip", ".zip", "PK", zip_open, zip_read, zip_write, zip_close }, 84 #define M_PACK (&c_table[4]) 85 { "pack", ".pak", "\037\036", pak_open, pak_read, pak_write, pak_close }, 86 #endif 87 { NULL } 88 }; 89 90 int permission(const char *); 91 void setfile(const char *, struct stat *); 92 __dead void usage(int); 93 int compress(const char *, char *, const struct compressor *, 94 int, struct stat *); 95 int decompress(const char *, char *, const struct compressor *, 96 int, struct stat *); 97 const struct compressor *check_method(int); 98 const char *check_suffix(const char *); 99 char *set_outfile(const char *, char *, size_t); 100 void list_stats(const char *, const struct compressor *, struct z_info *); 101 void verbose_info(const char *, off_t, off_t, u_int32_t); 102 103 #define OPTSTRING "123456789ab:cdfghlLnNOo:qrS:tvV" 104 const struct option longopts[] = { 105 { "ascii", no_argument, 0, 'a' }, 106 { "stdout", no_argument, 0, 'c' }, 107 { "to-stdout", no_argument, 0, 'c' }, 108 { "decompress", no_argument, 0, 'd' }, 109 { "uncompress", no_argument, 0, 'd' }, 110 { "force", no_argument, 0, 'f' }, 111 { "help", no_argument, 0, 'h' }, 112 { "list", no_argument, 0, 'l' }, 113 { "license", no_argument, 0, 'L' }, 114 { "no-name", no_argument, 0, 'n' }, 115 { "name", no_argument, 0, 'N' }, 116 { "quiet", no_argument, 0, 'q' }, 117 { "recursive", no_argument, 0, 'r' }, 118 { "suffix", required_argument, 0, 'S' }, 119 { "test", no_argument, 0, 't' }, 120 { "verbose", no_argument, 0, 'v' }, 121 { "version", no_argument, 0, 'V' }, 122 { "fast", no_argument, 0, '1' }, 123 { "best", no_argument, 0, '9' }, 124 { NULL } 125 }; 126 127 int 128 main(int argc, char *argv[]) 129 { 130 FTS *ftsp; 131 FTSENT *entry; 132 struct stat osb; 133 const struct compressor *method; 134 const char *s; 135 char *p, *infile; 136 char outfile[MAXPATHLEN], _infile[MAXPATHLEN], suffix[16]; 137 char *nargv[512]; /* some estimate based on ARG_MAX */ 138 int bits, exists, oreg, ch, error, i, rc, oflag; 139 140 bits = cat = oflag = decomp = 0; 141 nosave = -1; 142 p = __progname; 143 if (p[0] == 'g') { 144 method = M_DEFLATE; 145 bits = 6; 146 p++; 147 } else 148 method = M_COMPRESS; 149 150 decomp = 0; 151 if (!strcmp(p, "zcat")) { 152 decomp++; 153 cat = 1; 154 } else { 155 if (p[0] == 'u' && p[1] == 'n') { 156 p += 2; 157 decomp++; 158 } 159 160 if (strcmp(p, "zip") && 161 strcmp(p, "compress")) 162 errx(1, "unknown program name"); 163 } 164 165 strlcpy(suffix, method->suffix, sizeof(suffix)); 166 167 nargv[0] = NULL; 168 if ((p = getenv("GZIP")) != NULL) { 169 char *last; 170 171 nargv[0] = *argv++; 172 for (i = 1, (p = strtok_r(p, " ", &last)); p != NULL; 173 (p = strtok_r(NULL, " ", &last)), i++) 174 if (i < sizeof(nargv)/sizeof(nargv[1]) - argc - 1) 175 nargv[i] = p; 176 else { 177 errx(1, "GZIP is too long"); 178 } 179 argc += i - 1; 180 while ((nargv[i++] = *argv++)) 181 ; 182 argv = nargv; 183 } 184 185 while ((ch = getopt_long(argc, argv, OPTSTRING, longopts, NULL)) != -1) 186 switch(ch) { 187 case '1': 188 case '2': 189 case '3': 190 case '4': 191 case '5': 192 case '6': 193 case '7': 194 case '8': 195 case '9': 196 method = M_DEFLATE; 197 strlcpy(suffix, method->suffix, sizeof(suffix)); 198 bits = ch - '0'; 199 break; 200 case 'a': 201 warnx("option -a is ignored on this system"); 202 break; 203 case 'b': 204 bits = strtol(optarg, &p, 10); 205 /* 206 * POSIX 1002.3 says 9 <= bits <= 14 for portable 207 * apps, but says the implementation may allow 208 * greater. 209 */ 210 if (*p) 211 errx(1, "illegal bit count -- %s", optarg); 212 break; 213 case 'c': 214 cat = 1; 215 break; 216 case 'd': /* Backward compatible. */ 217 decomp++; 218 break; 219 case 'f': 220 force++; 221 break; 222 case 'g': 223 method = M_DEFLATE; 224 strlcpy(suffix, method->suffix, sizeof(suffix)); 225 bits = 6; 226 break; 227 case 'l': 228 list++; 229 testmode++; 230 decomp++; 231 break; 232 case 'n': 233 nosave = 1; 234 break; 235 case 'N': 236 nosave = 0; 237 break; 238 case 'O': 239 method = M_COMPRESS; 240 strlcpy(suffix, method->suffix, sizeof(suffix)); 241 break; 242 case 'o': 243 if (strlcpy(outfile, optarg, 244 sizeof(outfile)) >= sizeof(outfile)) 245 errx(1, "-o argument is too long"); 246 oflag = 1; 247 break; 248 case 'q': 249 verbose = -1; 250 break; 251 case 'S': 252 p = suffix; 253 if (optarg[0] != '.') 254 *p++ = '.'; 255 strlcpy(p, optarg, sizeof(suffix) - (p - suffix)); 256 p = optarg; 257 break; 258 case 't': 259 testmode = 1; 260 decomp++; 261 break; 262 case 'V': 263 printf("%s\n%s\n%s\n", main_rcsid, 264 z_rcsid, gz_rcsid); 265 exit (0); 266 case 'v': 267 verbose++; 268 break; 269 case 'L': 270 fputs(copyright, stderr); 271 fputs(license, stderr); 272 exit (0); 273 case 'r': 274 recurse++; 275 break; 276 277 case 'h': 278 usage(0); 279 break; 280 default: 281 usage(1); 282 } 283 argc -= optind; 284 argv += optind; 285 286 if (argc == 0) { 287 if (nargv[0] == NULL) 288 argv = nargv; 289 /* XXX - make sure we don't oflow nargv in $GZIP case (millert) */ 290 argv[0] = "/dev/stdin"; 291 argv[1] = NULL; 292 pipin++; 293 if (!oflag) 294 cat = 1; 295 } else { 296 for (i = 0; i < argc; i++) { 297 if (argv[i][0] == '-' && argv[i][1] == '\0') { 298 argv[i] = "/dev/stdin"; 299 pipin++; 300 cat = 1; 301 } 302 } 303 } 304 if (oflag && (recurse || argc > 1)) 305 errx(1, "-o option may only be used with a single input file"); 306 307 if ((cat && argc) + testmode + oflag > 1) 308 errx(1, "may not mix -o, -c, or -t options"); 309 if (nosave == -1) 310 nosave = decomp; 311 312 if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL) 313 err(1, NULL); 314 for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) { 315 infile = entry->fts_path; 316 switch (entry->fts_info) { 317 case FTS_D: 318 if (!recurse) { 319 warnx("%s is a directory: ignored", 320 infile); 321 fts_set(ftsp, entry, FTS_SKIP); 322 } 323 continue; 324 case FTS_DP: 325 continue; 326 case FTS_NS: 327 /* 328 * If file does not exist and has no suffix, 329 * tack on the default suffix and try that. 330 */ 331 /* XXX - is overwriting fts_statp legal? (millert) */ 332 if (entry->fts_errno == ENOENT && 333 strchr(entry->fts_accpath, '.') == NULL && 334 snprintf(_infile, sizeof(_infile), "%s%s", infile, 335 suffix) < sizeof(_infile) && 336 stat(_infile, entry->fts_statp) == 0 && 337 S_ISREG(entry->fts_statp->st_mode)) { 338 infile = _infile; 339 break; 340 } 341 case FTS_ERR: 342 case FTS_DNR: 343 warnx("%s: %s", infile, strerror(entry->fts_errno)); 344 rc = rc ? rc : WARNING; 345 continue; 346 default: 347 if (!S_ISREG(entry->fts_statp->st_mode) && !pipin) { 348 warnx("%s not a regular file%s", 349 infile, cat ? "" : ": unchanged"); 350 rc = rc ? rc : WARNING; 351 continue; 352 } 353 break; 354 } 355 356 if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) { 357 warnx("%s already has %s suffix -- unchanged", 358 infile, s); 359 rc = rc ? rc : WARNING; 360 continue; 361 } 362 363 if (cat) 364 strlcpy(outfile, "/dev/stdout", sizeof outfile); 365 else if (!oflag) { 366 if (decomp) { 367 if (set_outfile(infile, outfile, 368 sizeof outfile) == NULL) { 369 if (!recurse) 370 warnx("%s: unknown suffix: " 371 "ignored", infile); 372 continue; 373 } 374 } else { 375 if (snprintf(outfile, sizeof(outfile), 376 "%s%s", infile, suffix) >= sizeof(outfile)) { 377 warnx("%s%s: name too long", 378 infile, suffix); 379 continue; 380 } 381 } 382 } 383 384 exists = !stat(outfile, &osb); 385 if (!force && exists && S_ISREG(osb.st_mode) && 386 !permission(outfile)) { 387 rc = rc ? rc : WARNING; 388 continue; 389 } 390 391 oreg = !exists || S_ISREG(osb.st_mode); 392 393 if (verbose > 0 && !pipin && !list) 394 fprintf(stderr, "%s:\t", infile); 395 396 error = (decomp ? decompress : compress) 397 (infile, outfile, method, bits, entry->fts_statp); 398 399 switch (error) { 400 case SUCCESS: 401 if (!cat && !testmode) { 402 setfile(outfile, entry->fts_statp); 403 if (!pipin && unlink(infile) && verbose >= 0) 404 warn("input: %s", infile); 405 } 406 break; 407 case WARNING: 408 rc = rc ? rc : WARNING; 409 break; 410 default: 411 rc = FAILURE; 412 if (oreg && unlink(outfile) && errno != ENOENT && 413 verbose >= 0) { 414 if (force) 415 warn("output: %s", outfile); 416 else 417 err(1, "output: %s", outfile); 418 } 419 break; 420 } 421 } 422 if (list) 423 list_stats(NULL, NULL, NULL); 424 425 exit(rc); 426 } 427 428 int 429 compress(const char *in, char *out, const struct compressor *method, 430 int bits, struct stat *sb) 431 { 432 u_char buf[Z_BUFSIZE]; 433 char *name; 434 int error, ifd, ofd, flags; 435 void *cookie; 436 ssize_t nr; 437 u_int32_t mtime; 438 struct z_info info; 439 440 mtime = 0; 441 flags = 0; 442 error = SUCCESS; 443 name = NULL; 444 cookie = NULL; 445 446 if ((ifd = open(in, O_RDONLY)) < 0) { 447 if (verbose >= 0) 448 warn("%s", out); 449 return (FAILURE); 450 } 451 452 if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) { 453 if (verbose >= 0) 454 warn("%s", out); 455 (void) close(ifd); 456 return (FAILURE); 457 } 458 459 if (method != M_COMPRESS && !force && isatty(ofd)) { 460 if (verbose >= 0) 461 warnx("%s: won't write compressed data to terminal", 462 out); 463 (void) close(ofd); 464 (void) close(ifd); 465 return (FAILURE); 466 } 467 468 if (!pipin && !nosave) { 469 name = basename(in); 470 mtime = (u_int32_t)sb->st_mtime; 471 } 472 if ((cookie = (*method->open)(ofd, "w", name, bits, mtime, flags)) == NULL) { 473 if (verbose >= 0) 474 warn("%s", in); 475 (void) close(ofd); 476 (void) close(ifd); 477 return (FAILURE); 478 } 479 480 while ((nr = read(ifd, buf, sizeof(buf))) > 0) 481 if ((method->write)(cookie, buf, nr) != nr) { 482 if (verbose >= 0) 483 warn("%s", out); 484 error = FAILURE; 485 break; 486 } 487 488 if (!error && nr < 0) { 489 if (verbose >= 0) 490 warn("%s", in); 491 error = FAILURE; 492 } 493 494 if ((method->close)(cookie, &info)) { 495 if (!error && verbose >= 0) 496 warn("%s", out); 497 error = FAILURE; 498 } 499 500 if (close(ifd)) { 501 if (!error && verbose >= 0) 502 warn("%s", in); 503 error = FAILURE; 504 } 505 506 if (!force && info.total_out >= info.total_in) { 507 if (verbose > 0) 508 fprintf(stderr, "file would grow; left unmodified\n"); 509 error = FAILURE; 510 } 511 512 if (!error && verbose > 0) 513 verbose_info(out, info.total_out, info.total_in, info.hlen); 514 515 return (error); 516 } 517 518 const struct compressor * 519 check_method(int fd) 520 { 521 const struct compressor *method; 522 u_char magic[2]; 523 524 if (read(fd, magic, sizeof(magic)) != 2) 525 return (NULL); 526 for (method = &c_table[0]; method->name != NULL; method++) { 527 if (magic[0] == method->magic[0] && 528 magic[1] == method->magic[1]) 529 return (method); 530 } 531 return (NULL); 532 } 533 534 int 535 decompress(const char *in, char *out, const struct compressor *method, 536 int bits, struct stat *sb) 537 { 538 u_char buf[Z_BUFSIZE]; 539 int error, ifd, ofd; 540 void *cookie; 541 ssize_t nr; 542 struct z_info info; 543 544 error = SUCCESS; 545 cookie = NULL; 546 547 if ((ifd = open(in, O_RDONLY)) < 0) { 548 if (verbose >= 0) 549 warn("%s", in); 550 return -1; 551 } 552 553 if (!force && isatty(ifd)) { 554 if (verbose >= 0) 555 warnx("%s: won't read compressed data from terminal", 556 in); 557 close (ifd); 558 return -1; 559 } 560 561 if ((method = check_method(ifd)) == NULL) { 562 if (verbose >= 0) 563 warnx("%s: unrecognized file format", in); 564 close (ifd); 565 return -1; 566 } 567 568 /* XXX - open constrains outfile to MAXPATHLEN so this is safe */ 569 if ((cookie = (*method->open)(ifd, "r", nosave ? NULL : out, 570 bits, 0, 1)) == NULL) { 571 if (verbose >= 0) 572 warn("%s", in); 573 close (ifd); 574 return (FAILURE); 575 } 576 577 if (testmode) 578 ofd = -1; 579 else if ((ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR)) < 0) { 580 if (verbose >= 0) 581 warn("%s", in); 582 (method->close)(cookie, NULL); 583 return (FAILURE); 584 } 585 586 while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) { 587 if (ofd != -1 && write(ofd, buf, nr) != nr) { 588 if (verbose >= 0) 589 warn("%s", out); 590 error = FAILURE; 591 break; 592 } 593 } 594 595 if (!error && nr < 0) { 596 if (verbose >= 0) 597 warnx("%s: %s", in, 598 errno == EINVAL ? "crc error" : strerror(errno)); 599 error = errno == EINVAL ? WARNING : FAILURE; 600 } 601 602 if ((method->close)(cookie, &info)) { 603 if (!error && verbose >= 0) 604 warnx("%s", in); 605 error = FAILURE; 606 } 607 608 if (!nosave) { 609 if (info.mtime != 0) { 610 sb->st_mtimespec.tv_sec = 611 sb->st_atimespec.tv_sec = info.mtime; 612 sb->st_mtimespec.tv_nsec = 613 sb->st_atimespec.tv_nsec = 0; 614 } else 615 nosave = 1; /* no timestamp to restore */ 616 617 if (cat && strcmp(out, "/dev/stdout") != 0) 618 cat = 0; /* have a real output name */ 619 } 620 621 if (ofd != -1 && close(ofd)) { 622 if (!error && verbose >= 0) 623 warn("%s", out); 624 error = FAILURE; 625 } 626 627 if (!error) { 628 if (list) { 629 if (info.mtime == 0) 630 info.mtime = (u_int32_t)sb->st_mtime; 631 list_stats(out, method, &info); 632 } else if (verbose > 0) { 633 verbose_info(out, info.total_in, info.total_out, 634 info.hlen); 635 } 636 } 637 638 return (error); 639 } 640 641 void 642 setfile(const char *name, struct stat *fs) 643 { 644 struct timeval tv[2]; 645 646 if (!pipin || !nosave) { 647 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 648 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 649 if (utimes(name, tv)) 650 warn("utimes: %s", name); 651 } 652 653 /* 654 * If input was a pipe we don't have any info to restore but we 655 * must set the mode since the current mode on the file is 0200. 656 */ 657 if (pipin) { 658 mode_t mask = umask(022); 659 chmod(name, DEFFILEMODE & ~mask); 660 umask(mask); 661 return; 662 } 663 664 /* 665 * Changing the ownership probably won't succeed, unless we're root 666 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 667 * the mode; current BSD behavior is to remove all setuid bits on 668 * chown. If chown fails, lose setuid/setgid bits. 669 */ 670 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 671 if (chown(name, fs->st_uid, fs->st_gid)) { 672 if (errno != EPERM) 673 warn("chown: %s", name); 674 fs->st_mode &= ~(S_ISUID|S_ISGID); 675 } 676 if (chmod(name, fs->st_mode)) 677 warn("chown: %s", name); 678 679 if (fs->st_flags && chflags(name, fs->st_flags)) 680 warn("chflags: %s", name); 681 } 682 683 int 684 permission(const char *fname) 685 { 686 int ch, first; 687 688 if (!isatty(fileno(stderr))) 689 return (0); 690 (void)fprintf(stderr, "overwrite %s? ", fname); 691 first = ch = getchar(); 692 while (ch != '\n' && ch != EOF) 693 ch = getchar(); 694 return (first == 'y'); 695 } 696 697 /* 698 * Check infile for a known suffix and return the suffix portion or NULL. 699 */ 700 const char * 701 check_suffix(const char *infile) 702 { 703 int i; 704 char *suf, *sep, *separators = ".-_"; 705 static char *suffixes[] = { "Z", "gz", "z", "tgz", "taz", NULL }; 706 707 for (sep = separators; *sep != '\0'; sep++) { 708 if ((suf = strrchr(infile, *sep)) == NULL) 709 continue; 710 suf++; 711 712 for (i = 0; suffixes[i] != NULL; i++) { 713 if (strcmp(suf, suffixes[i]) == 0) 714 return (suf - 1); 715 } 716 } 717 return (NULL); 718 } 719 720 /* 721 * Set outfile based on the suffix. In most cases we just strip 722 * off the suffix but things like .tgz and .taz are special. 723 */ 724 char * 725 set_outfile(const char *infile, char *outfile, size_t osize) 726 { 727 const char *s; 728 char *cp; 729 730 if ((s = check_suffix(infile)) == NULL) 731 return (NULL); 732 733 (void)strlcpy(outfile, infile, osize); 734 cp = outfile + (s - infile) + 1; 735 /* 736 * Convert tgz and taz -> tar, else drop the suffix. 737 */ 738 if (strcmp(cp, "tgz") == 0) { 739 cp[1] = 'a'; 740 cp[2] = 'r'; 741 } else if (strcmp(cp, "taz") == 0) 742 cp[2] = 'r'; 743 else 744 cp[-1] = '\0'; 745 return (outfile); 746 } 747 748 /* 749 * Print output for the -l option. 750 */ 751 void 752 list_stats(const char *name, const struct compressor *method, 753 struct z_info *info) 754 { 755 static off_t compressed_total, uncompressed_total, header_total; 756 static u_int nruns; 757 char *timestr; 758 759 if (name != NULL && strcmp(name, "/dev/stdout") == 0) 760 name += 5; 761 762 if (nruns == 0) { 763 if (verbose >= 0) { 764 if (verbose > 0) 765 fputs("method crc date time ", stdout); 766 puts("compressed uncompr. ratio uncompressed_name"); 767 } 768 } 769 nruns++; 770 771 if (name != NULL) { 772 if (verbose > 0) { 773 timestr = ctime(&info->mtime) + 4; 774 timestr[12] = '\0'; 775 printf("%.5s %08x %s ", method->name, info->crc, timestr); 776 } 777 printf("%9lld %9lld %4.1f%% %s\n", 778 (long long)(info->total_in + info->hlen), 779 (long long)info->total_out, 780 (info->total_out - info->total_in) * 781 100.0 / info->total_out, name); 782 compressed_total += info->total_in; 783 uncompressed_total += info->total_out; 784 header_total += info->hlen; 785 } else if (verbose >= 0) { 786 if (nruns < 3) /* only do totals for > 1 files */ 787 return; 788 if (verbose > 0) 789 fputs(" ", stdout); 790 printf("%9lld %9lld %4.1f%% (totals)\n", 791 (long long)(compressed_total + header_total), 792 (long long)uncompressed_total, 793 (uncompressed_total - compressed_total) * 794 100.0 / uncompressed_total); 795 } 796 } 797 798 void 799 verbose_info(const char *file, off_t compressed, off_t uncompressed, 800 u_int32_t hlen) 801 { 802 if (testmode) { 803 fputs("OK\n", stderr); 804 return; 805 } 806 if (!pipin) { 807 fprintf(stderr, "\t%4.1f%% -- replaced with %s\n", 808 (uncompressed - compressed) * 100.0 / uncompressed, file); 809 } 810 compressed += hlen; 811 fprintf(stderr, "%lld bytes in, %lld bytes out\n", 812 (long long)(decomp ? compressed : uncompressed), 813 (long long)(decomp ? uncompressed : compressed)); 814 } 815 816 __dead void 817 usage(int status) 818 { 819 fprintf(stderr, 820 "usage: %s [-cdfghOqrtvV] [-b bits] [-S suffix] [-[1-9]] [file ...]\n", 821 __progname); 822 exit(status); 823 } 824