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