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