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