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