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