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