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