1 /* $NetBSD: gzip.c,v 1.89 2006/11/13 21:57:59 mrg Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n\ 35 All rights reserved.\n"); 36 __RCSID("$FreeBSD$"); 37 #endif /* not lint */ 38 39 /* 40 * gzip.c -- GPL free gzip using zlib. 41 * 42 * RFC 1950 covers the zlib format 43 * RFC 1951 covers the deflate format 44 * RFC 1952 covers the gzip format 45 * 46 * TODO: 47 * - use mmap where possible 48 * - handle some signals better (remove outfile?) 49 * - make bzip2/compress -v/-t/-l support work as well as possible 50 */ 51 52 #include <sys/param.h> 53 #include <sys/stat.h> 54 #include <sys/time.h> 55 56 #include <inttypes.h> 57 #include <unistd.h> 58 #include <stdio.h> 59 #include <string.h> 60 #include <stdlib.h> 61 #include <err.h> 62 #include <errno.h> 63 #include <fcntl.h> 64 #include <zlib.h> 65 #include <fts.h> 66 #include <libgen.h> 67 #include <stdarg.h> 68 #include <getopt.h> 69 #include <time.h> 70 71 #ifndef PRIdOFF 72 #define PRIdOFF PRId64 73 #endif 74 75 /* what type of file are we dealing with */ 76 enum filetype { 77 FT_GZIP, 78 #ifndef NO_BZIP2_SUPPORT 79 FT_BZIP2, 80 #endif 81 #ifndef NO_COMPRESS_SUPPORT 82 FT_Z, 83 #endif 84 FT_LAST, 85 FT_UNKNOWN 86 }; 87 88 #ifndef NO_BZIP2_SUPPORT 89 #include <bzlib.h> 90 91 #define BZ2_SUFFIX ".bz2" 92 #define BZIP2_MAGIC "\102\132\150" 93 #endif 94 95 #ifndef NO_COMPRESS_SUPPORT 96 #define Z_SUFFIX ".Z" 97 #define Z_MAGIC "\037\235" 98 #endif 99 100 #define GZ_SUFFIX ".gz" 101 102 #define BUFLEN (64 * 1024) 103 104 #define GZIP_MAGIC0 0x1F 105 #define GZIP_MAGIC1 0x8B 106 #define GZIP_OMAGIC1 0x9E 107 108 #define GZIP_TIMESTAMP (off_t)4 109 #define GZIP_ORIGNAME (off_t)10 110 111 #define HEAD_CRC 0x02 112 #define EXTRA_FIELD 0x04 113 #define ORIG_NAME 0x08 114 #define COMMENT 0x10 115 116 #define OS_CODE 3 /* Unix */ 117 118 typedef struct { 119 const char *zipped; 120 int ziplen; 121 const char *normal; /* for unzip - must not be longer than zipped */ 122 } suffixes_t; 123 static suffixes_t suffixes[] = { 124 #define SUFFIX(Z, N) {Z, sizeof Z - 1, N} 125 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ 126 #ifndef SMALL 127 SUFFIX(GZ_SUFFIX, ""), 128 SUFFIX(".z", ""), 129 SUFFIX("-gz", ""), 130 SUFFIX("-z", ""), 131 SUFFIX("_z", ""), 132 SUFFIX(".taz", ".tar"), 133 SUFFIX(".tgz", ".tar"), 134 #ifndef NO_BZIP2_SUPPORT 135 SUFFIX(BZ2_SUFFIX, ""), 136 #endif 137 #ifndef NO_COMPRESS_SUPPORT 138 SUFFIX(Z_SUFFIX, ""), 139 #endif 140 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ 141 #endif /* SMALL */ 142 #undef SUFFIX 143 }; 144 #define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) 145 146 static const char gzip_version[] = "FreeBSD gzip 20070126"; 147 148 #ifndef SMALL 149 static const char gzip_copyright[] = \ 150 " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" 151 " All rights reserved.\n" 152 "\n" 153 " Redistribution and use in source and binary forms, with or without\n" 154 " modification, are permitted provided that the following conditions\n" 155 " are met:\n" 156 " 1. Redistributions of source code must retain the above copyright\n" 157 " notice, this list of conditions and the following disclaimer.\n" 158 " 2. Redistributions in binary form must reproduce the above copyright\n" 159 " notice, this list of conditions and the following disclaimer in the\n" 160 " documentation and/or other materials provided with the distribution.\n" 161 " 3. The name of the author may not be used to endorse or promote products\n" 162 " derived from this software without specific prior written permission.\n" 163 "\n" 164 " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 165 " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 166 " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 167 " IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" 168 " INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" 169 " BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" 170 " LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" 171 " AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" 172 " OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" 173 " OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" 174 " SUCH DAMAGE."; 175 #endif 176 177 static int cflag; /* stdout mode */ 178 static int dflag; /* decompress mode */ 179 static int lflag; /* list mode */ 180 static int numflag = 6; /* gzip -1..-9 value */ 181 182 #ifndef SMALL 183 static int fflag; /* force mode */ 184 static int nflag; /* don't save name/timestamp */ 185 static int Nflag; /* don't restore name/timestamp */ 186 static int qflag; /* quiet mode */ 187 static int rflag; /* recursive mode */ 188 static int tflag; /* test */ 189 static int vflag; /* verbose mode */ 190 #else 191 #define qflag 0 192 #define tflag 0 193 #endif 194 195 static int exit_value = 0; /* exit value */ 196 197 static char *infile; /* name of file coming in */ 198 199 static void maybe_err(const char *fmt, ...) 200 __attribute__((__format__(__printf__, 1, 2))); 201 #ifndef NO_BZIP2_SUPPORT 202 static void maybe_errx(const char *fmt, ...) 203 __attribute__((__format__(__printf__, 1, 2))); 204 #endif 205 static void maybe_warn(const char *fmt, ...) 206 __attribute__((__format__(__printf__, 1, 2))); 207 static void maybe_warnx(const char *fmt, ...) 208 __attribute__((__format__(__printf__, 1, 2))); 209 static enum filetype file_gettype(u_char *); 210 #ifdef SMALL 211 #define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) 212 #endif 213 static off_t gz_compress(int, int, off_t *, const char *, uint32_t); 214 static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); 215 static off_t file_compress(char *, char *, size_t); 216 static off_t file_uncompress(char *, char *, size_t); 217 static void handle_pathname(char *); 218 static void handle_file(char *, struct stat *); 219 static void handle_stdin(void); 220 static void handle_stdout(void); 221 static void print_ratio(off_t, off_t, FILE *); 222 static void print_list(int fd, off_t, const char *, time_t); 223 static void usage(void); 224 static void display_version(void); 225 #ifndef SMALL 226 static void display_license(void); 227 #endif 228 static const suffixes_t *check_suffix(char *, int); 229 static ssize_t read_retry(int, void *, size_t); 230 231 #ifdef SMALL 232 #define unlink_input(f, sb) unlink(f) 233 #else 234 static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); 235 static void prepend_gzip(char *, int *, char ***); 236 static void handle_dir(char *); 237 static void print_verbage(const char *, const char *, off_t, off_t); 238 static void print_test(const char *, int); 239 static void copymodes(int fd, const struct stat *, const char *file); 240 static int check_outfile(const char *outfile); 241 #endif 242 243 #ifndef NO_BZIP2_SUPPORT 244 static off_t unbzip2(int, int, char *, size_t, off_t *); 245 #endif 246 247 #ifndef NO_COMPRESS_SUPPORT 248 static FILE *zdopen(int); 249 static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); 250 #endif 251 252 int main(int, char **p); 253 254 #ifdef SMALL 255 #define getopt_long(a,b,c,d,e) getopt(a,b,c) 256 #else 257 static const struct option longopts[] = { 258 { "stdout", no_argument, 0, 'c' }, 259 { "to-stdout", no_argument, 0, 'c' }, 260 { "decompress", no_argument, 0, 'd' }, 261 { "uncompress", no_argument, 0, 'd' }, 262 { "force", no_argument, 0, 'f' }, 263 { "help", no_argument, 0, 'h' }, 264 { "list", no_argument, 0, 'l' }, 265 { "no-name", no_argument, 0, 'n' }, 266 { "name", no_argument, 0, 'N' }, 267 { "quiet", no_argument, 0, 'q' }, 268 { "recursive", no_argument, 0, 'r' }, 269 { "suffix", required_argument, 0, 'S' }, 270 { "test", no_argument, 0, 't' }, 271 { "verbose", no_argument, 0, 'v' }, 272 { "version", no_argument, 0, 'V' }, 273 { "fast", no_argument, 0, '1' }, 274 { "best", no_argument, 0, '9' }, 275 { "ascii", no_argument, 0, 'a' }, 276 { "license", no_argument, 0, 'L' }, 277 { NULL, no_argument, 0, 0 }, 278 }; 279 #endif 280 281 int 282 main(int argc, char **argv) 283 { 284 const char *progname = getprogname(); 285 #ifndef SMALL 286 char *gzip; 287 int len; 288 #endif 289 int ch; 290 291 /* XXX set up signals */ 292 293 #ifndef SMALL 294 if ((gzip = getenv("GZIP")) != NULL) 295 prepend_gzip(gzip, &argc, &argv); 296 #endif 297 298 /* 299 * XXX 300 * handle being called `gunzip', `zcat' and `gzcat' 301 */ 302 if (strcmp(progname, "gunzip") == 0) 303 dflag = 1; 304 else if (strcmp(progname, "zcat") == 0 || 305 strcmp(progname, "gzcat") == 0) 306 dflag = cflag = 1; 307 308 #ifdef SMALL 309 #define OPT_LIST "123456789cdhltV" 310 #else 311 #define OPT_LIST "123456789acdfhlLNnqrS:tVv" 312 #endif 313 314 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { 315 switch (ch) { 316 case '1': case '2': case '3': 317 case '4': case '5': case '6': 318 case '7': case '8': case '9': 319 numflag = ch - '0'; 320 break; 321 case 'c': 322 cflag = 1; 323 break; 324 case 'd': 325 dflag = 1; 326 break; 327 case 'l': 328 lflag = 1; 329 dflag = 1; 330 break; 331 case 'V': 332 display_version(); 333 /* NOTREACHED */ 334 #ifndef SMALL 335 case 'a': 336 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname); 337 break; 338 case 'f': 339 fflag = 1; 340 break; 341 case 'L': 342 display_license(); 343 /* NOT REACHED */ 344 case 'N': 345 nflag = 0; 346 Nflag = 1; 347 break; 348 case 'n': 349 nflag = 1; 350 Nflag = 0; 351 break; 352 case 'q': 353 qflag = 1; 354 break; 355 case 'r': 356 rflag = 1; 357 break; 358 case 'S': 359 len = strlen(optarg); 360 if (len != 0) { 361 suffixes[0].zipped = optarg; 362 suffixes[0].ziplen = len; 363 } else { 364 suffixes[NUM_SUFFIXES - 1].zipped = ""; 365 suffixes[NUM_SUFFIXES - 1].ziplen = 0; 366 } 367 break; 368 case 't': 369 cflag = 1; 370 tflag = 1; 371 dflag = 1; 372 break; 373 case 'v': 374 vflag = 1; 375 break; 376 #endif 377 default: 378 usage(); 379 /* NOTREACHED */ 380 } 381 } 382 argv += optind; 383 argc -= optind; 384 385 if (argc == 0) { 386 if (dflag) /* stdin mode */ 387 handle_stdin(); 388 else /* stdout mode */ 389 handle_stdout(); 390 } else { 391 do { 392 handle_pathname(argv[0]); 393 } while (*++argv); 394 } 395 #ifndef SMALL 396 if (qflag == 0 && lflag && argc > 1) 397 print_list(-1, 0, "(totals)", 0); 398 #endif 399 exit(exit_value); 400 } 401 402 /* maybe print a warning */ 403 void 404 maybe_warn(const char *fmt, ...) 405 { 406 va_list ap; 407 408 if (qflag == 0) { 409 va_start(ap, fmt); 410 vwarn(fmt, ap); 411 va_end(ap); 412 } 413 if (exit_value == 0) 414 exit_value = 1; 415 } 416 417 /* ... without an errno. */ 418 void 419 maybe_warnx(const char *fmt, ...) 420 { 421 va_list ap; 422 423 if (qflag == 0) { 424 va_start(ap, fmt); 425 vwarnx(fmt, ap); 426 va_end(ap); 427 } 428 if (exit_value == 0) 429 exit_value = 1; 430 } 431 432 /* maybe print an error */ 433 void 434 maybe_err(const char *fmt, ...) 435 { 436 va_list ap; 437 438 if (qflag == 0) { 439 va_start(ap, fmt); 440 vwarn(fmt, ap); 441 va_end(ap); 442 } 443 exit(2); 444 } 445 446 #ifndef NO_BZIP2_SUPPORT 447 /* ... without an errno. */ 448 void 449 maybe_errx(const char *fmt, ...) 450 { 451 va_list ap; 452 453 if (qflag == 0) { 454 va_start(ap, fmt); 455 vwarnx(fmt, ap); 456 va_end(ap); 457 } 458 exit(2); 459 } 460 #endif 461 462 #ifndef SMALL 463 /* split up $GZIP and prepend it to the argument list */ 464 static void 465 prepend_gzip(char *gzip, int *argc, char ***argv) 466 { 467 char *s, **nargv, **ac; 468 int nenvarg = 0, i; 469 470 /* scan how many arguments there are */ 471 for (s = gzip;;) { 472 while (*s == ' ' || *s == '\t') 473 s++; 474 if (*s == 0) 475 goto count_done; 476 nenvarg++; 477 while (*s != ' ' && *s != '\t') 478 if (*s++ == 0) 479 goto count_done; 480 } 481 count_done: 482 /* punt early */ 483 if (nenvarg == 0) 484 return; 485 486 *argc += nenvarg; 487 ac = *argv; 488 489 nargv = (char **)malloc((*argc + 1) * sizeof(char *)); 490 if (nargv == NULL) 491 maybe_err("malloc"); 492 493 /* stash this away */ 494 *argv = nargv; 495 496 /* copy the program name first */ 497 i = 0; 498 nargv[i++] = *(ac++); 499 500 /* take a copy of $GZIP and add it to the array */ 501 s = strdup(gzip); 502 if (s == NULL) 503 maybe_err("strdup"); 504 for (;;) { 505 /* Skip whitespaces. */ 506 while (*s == ' ' || *s == '\t') 507 s++; 508 if (*s == 0) 509 goto copy_done; 510 nargv[i++] = s; 511 /* Find the end of this argument. */ 512 while (*s != ' ' && *s != '\t') 513 if (*s++ == 0) 514 /* Argument followed by NUL. */ 515 goto copy_done; 516 /* Terminate by overwriting ' ' or '\t' with NUL. */ 517 *s++ = 0; 518 } 519 copy_done: 520 521 /* copy the original arguments and a NULL */ 522 while (*ac) 523 nargv[i++] = *(ac++); 524 nargv[i] = NULL; 525 } 526 #endif 527 528 /* compress input to output. Return bytes read, -1 on error */ 529 static off_t 530 gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) 531 { 532 z_stream z; 533 char *outbufp, *inbufp; 534 off_t in_tot = 0, out_tot = 0; 535 ssize_t in_size; 536 int i, error; 537 uLong crc; 538 #ifdef SMALL 539 static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, 540 0, 0, 0, 0, 541 0, OS_CODE }; 542 #endif 543 544 outbufp = malloc(BUFLEN); 545 inbufp = malloc(BUFLEN); 546 if (outbufp == NULL || inbufp == NULL) { 547 maybe_err("malloc failed"); 548 goto out; 549 } 550 551 memset(&z, 0, sizeof z); 552 z.zalloc = Z_NULL; 553 z.zfree = Z_NULL; 554 z.opaque = 0; 555 556 #ifdef SMALL 557 memcpy(outbufp, header, sizeof header); 558 i = sizeof header; 559 #else 560 if (nflag != 0) { 561 mtime = 0; 562 origname = ""; 563 } 564 565 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", 566 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 567 *origname ? ORIG_NAME : 0, 568 mtime & 0xff, 569 (mtime >> 8) & 0xff, 570 (mtime >> 16) & 0xff, 571 (mtime >> 24) & 0xff, 572 numflag == 1 ? 4 : numflag == 9 ? 2 : 0, 573 OS_CODE, origname); 574 if (i >= BUFLEN) 575 /* this need PATH_MAX > BUFLEN ... */ 576 maybe_err("snprintf"); 577 if (*origname) 578 i++; 579 #endif 580 581 z.next_out = (unsigned char *)outbufp + i; 582 z.avail_out = BUFLEN - i; 583 584 error = deflateInit2(&z, numflag, Z_DEFLATED, 585 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); 586 if (error != Z_OK) { 587 maybe_warnx("deflateInit2 failed"); 588 in_tot = -1; 589 goto out; 590 } 591 592 crc = crc32(0L, Z_NULL, 0); 593 for (;;) { 594 if (z.avail_out == 0) { 595 if (write(out, outbufp, BUFLEN) != BUFLEN) { 596 maybe_warn("write"); 597 out_tot = -1; 598 goto out; 599 } 600 601 out_tot += BUFLEN; 602 z.next_out = (unsigned char *)outbufp; 603 z.avail_out = BUFLEN; 604 } 605 606 if (z.avail_in == 0) { 607 in_size = read(in, inbufp, BUFLEN); 608 if (in_size < 0) { 609 maybe_warn("read"); 610 in_tot = -1; 611 goto out; 612 } 613 if (in_size == 0) 614 break; 615 616 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); 617 in_tot += in_size; 618 z.next_in = (unsigned char *)inbufp; 619 z.avail_in = in_size; 620 } 621 622 error = deflate(&z, Z_NO_FLUSH); 623 if (error != Z_OK && error != Z_STREAM_END) { 624 maybe_warnx("deflate failed"); 625 in_tot = -1; 626 goto out; 627 } 628 } 629 630 /* clean up */ 631 for (;;) { 632 size_t len; 633 ssize_t w; 634 635 error = deflate(&z, Z_FINISH); 636 if (error != Z_OK && error != Z_STREAM_END) { 637 maybe_warnx("deflate failed"); 638 in_tot = -1; 639 goto out; 640 } 641 642 len = (char *)z.next_out - outbufp; 643 644 w = write(out, outbufp, len); 645 if (w == -1 || (size_t)w != len) { 646 maybe_warn("write"); 647 out_tot = -1; 648 goto out; 649 } 650 out_tot += len; 651 z.next_out = (unsigned char *)outbufp; 652 z.avail_out = BUFLEN; 653 654 if (error == Z_STREAM_END) 655 break; 656 } 657 658 if (deflateEnd(&z) != Z_OK) { 659 maybe_warnx("deflateEnd failed"); 660 in_tot = -1; 661 goto out; 662 } 663 664 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 665 (int)crc & 0xff, 666 (int)(crc >> 8) & 0xff, 667 (int)(crc >> 16) & 0xff, 668 (int)(crc >> 24) & 0xff, 669 (int)in_tot & 0xff, 670 (int)(in_tot >> 8) & 0xff, 671 (int)(in_tot >> 16) & 0xff, 672 (int)(in_tot >> 24) & 0xff); 673 if (i != 8) 674 maybe_err("snprintf"); 675 if (write(out, outbufp, i) != i) { 676 maybe_warn("write"); 677 in_tot = -1; 678 } else 679 out_tot += i; 680 681 out: 682 if (inbufp != NULL) 683 free(inbufp); 684 if (outbufp != NULL) 685 free(outbufp); 686 if (gsizep) 687 *gsizep = out_tot; 688 return in_tot; 689 } 690 691 /* 692 * uncompress input to output then close the input. return the 693 * uncompressed size written, and put the compressed sized read 694 * into `*gsizep'. 695 */ 696 static off_t 697 gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, 698 const char *filename) 699 { 700 z_stream z; 701 char *outbufp, *inbufp; 702 off_t out_tot = -1, in_tot = 0; 703 uint32_t out_sub_tot = 0; 704 enum { 705 GZSTATE_MAGIC0, 706 GZSTATE_MAGIC1, 707 GZSTATE_METHOD, 708 GZSTATE_FLAGS, 709 GZSTATE_SKIPPING, 710 GZSTATE_EXTRA, 711 GZSTATE_EXTRA2, 712 GZSTATE_EXTRA3, 713 GZSTATE_ORIGNAME, 714 GZSTATE_COMMENT, 715 GZSTATE_HEAD_CRC1, 716 GZSTATE_HEAD_CRC2, 717 GZSTATE_INIT, 718 GZSTATE_READ, 719 GZSTATE_CRC, 720 GZSTATE_LEN, 721 } state = GZSTATE_MAGIC0; 722 int flags = 0, skip_count = 0; 723 int error = Z_STREAM_ERROR, done_reading = 0; 724 uLong crc = 0; 725 ssize_t wr; 726 int needmore = 0; 727 728 #define ADVANCE() { z.next_in++; z.avail_in--; } 729 730 if ((outbufp = malloc(BUFLEN)) == NULL) { 731 maybe_err("malloc failed"); 732 goto out2; 733 } 734 if ((inbufp = malloc(BUFLEN)) == NULL) { 735 maybe_err("malloc failed"); 736 goto out1; 737 } 738 739 memset(&z, 0, sizeof z); 740 z.avail_in = prelen; 741 z.next_in = (unsigned char *)pre; 742 z.avail_out = BUFLEN; 743 z.next_out = (unsigned char *)outbufp; 744 z.zalloc = NULL; 745 z.zfree = NULL; 746 z.opaque = 0; 747 748 in_tot = prelen; 749 out_tot = 0; 750 751 for (;;) { 752 if ((z.avail_in == 0 || needmore) && done_reading == 0) { 753 ssize_t in_size; 754 755 if (z.avail_in > 0) { 756 memmove(inbufp, z.next_in, z.avail_in); 757 } 758 z.next_in = (unsigned char *)inbufp; 759 in_size = read(in, z.next_in + z.avail_in, 760 BUFLEN - z.avail_in); 761 762 if (in_size == -1) { 763 maybe_warn("failed to read stdin"); 764 goto stop_and_fail; 765 } else if (in_size == 0) { 766 done_reading = 1; 767 } 768 769 z.avail_in += in_size; 770 needmore = 0; 771 772 in_tot += in_size; 773 } 774 if (z.avail_in == 0) { 775 if (done_reading && state != GZSTATE_MAGIC0) { 776 maybe_warnx("%s: unexpected end of file", 777 filename); 778 goto stop_and_fail; 779 } 780 goto stop; 781 } 782 switch (state) { 783 case GZSTATE_MAGIC0: 784 if (*z.next_in != GZIP_MAGIC0) { 785 if (in_tot > 0) { 786 maybe_warnx("%s: trailing garbage " 787 "ignored", filename); 788 goto stop; 789 } 790 maybe_warnx("input not gziped (MAGIC0)"); 791 goto stop_and_fail; 792 } 793 ADVANCE(); 794 state++; 795 out_sub_tot = 0; 796 crc = crc32(0L, Z_NULL, 0); 797 break; 798 799 case GZSTATE_MAGIC1: 800 if (*z.next_in != GZIP_MAGIC1 && 801 *z.next_in != GZIP_OMAGIC1) { 802 maybe_warnx("input not gziped (MAGIC1)"); 803 goto stop_and_fail; 804 } 805 ADVANCE(); 806 state++; 807 break; 808 809 case GZSTATE_METHOD: 810 if (*z.next_in != Z_DEFLATED) { 811 maybe_warnx("unknown compression method"); 812 goto stop_and_fail; 813 } 814 ADVANCE(); 815 state++; 816 break; 817 818 case GZSTATE_FLAGS: 819 flags = *z.next_in; 820 ADVANCE(); 821 skip_count = 6; 822 state++; 823 break; 824 825 case GZSTATE_SKIPPING: 826 if (skip_count > 0) { 827 skip_count--; 828 ADVANCE(); 829 } else 830 state++; 831 break; 832 833 case GZSTATE_EXTRA: 834 if ((flags & EXTRA_FIELD) == 0) { 835 state = GZSTATE_ORIGNAME; 836 break; 837 } 838 skip_count = *z.next_in; 839 ADVANCE(); 840 state++; 841 break; 842 843 case GZSTATE_EXTRA2: 844 skip_count |= ((*z.next_in) << 8); 845 ADVANCE(); 846 state++; 847 break; 848 849 case GZSTATE_EXTRA3: 850 if (skip_count > 0) { 851 skip_count--; 852 ADVANCE(); 853 } else 854 state++; 855 break; 856 857 case GZSTATE_ORIGNAME: 858 if ((flags & ORIG_NAME) == 0) { 859 state++; 860 break; 861 } 862 if (*z.next_in == 0) 863 state++; 864 ADVANCE(); 865 break; 866 867 case GZSTATE_COMMENT: 868 if ((flags & COMMENT) == 0) { 869 state++; 870 break; 871 } 872 if (*z.next_in == 0) 873 state++; 874 ADVANCE(); 875 break; 876 877 case GZSTATE_HEAD_CRC1: 878 if (flags & HEAD_CRC) 879 skip_count = 2; 880 else 881 skip_count = 0; 882 state++; 883 break; 884 885 case GZSTATE_HEAD_CRC2: 886 if (skip_count > 0) { 887 skip_count--; 888 ADVANCE(); 889 } else 890 state++; 891 break; 892 893 case GZSTATE_INIT: 894 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 895 maybe_warnx("failed to inflateInit"); 896 goto stop_and_fail; 897 } 898 state++; 899 break; 900 901 case GZSTATE_READ: 902 error = inflate(&z, Z_FINISH); 903 switch (error) { 904 /* Z_BUF_ERROR goes with Z_FINISH... */ 905 case Z_BUF_ERROR: 906 case Z_STREAM_END: 907 case Z_OK: 908 break; 909 910 case Z_NEED_DICT: 911 maybe_warnx("Z_NEED_DICT error"); 912 goto stop_and_fail; 913 case Z_DATA_ERROR: 914 maybe_warnx("data stream error"); 915 goto stop_and_fail; 916 case Z_STREAM_ERROR: 917 maybe_warnx("internal stream error"); 918 goto stop_and_fail; 919 case Z_MEM_ERROR: 920 maybe_warnx("memory allocation error"); 921 goto stop_and_fail; 922 923 default: 924 maybe_warn("unknown error from inflate(): %d", 925 error); 926 } 927 wr = BUFLEN - z.avail_out; 928 929 if (wr != 0) { 930 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); 931 if ( 932 #ifndef SMALL 933 /* don't write anything with -t */ 934 tflag == 0 && 935 #endif 936 write(out, outbufp, wr) != wr) { 937 maybe_warn("error writing to output"); 938 goto stop_and_fail; 939 } 940 941 out_tot += wr; 942 out_sub_tot += wr; 943 } 944 945 if (error == Z_STREAM_END) { 946 inflateEnd(&z); 947 state++; 948 } 949 950 z.next_out = (unsigned char *)outbufp; 951 z.avail_out = BUFLEN; 952 953 break; 954 case GZSTATE_CRC: 955 { 956 uLong origcrc; 957 958 if (z.avail_in < 4) { 959 if (!done_reading) { 960 needmore = 1; 961 continue; 962 } 963 maybe_warnx("truncated input"); 964 goto stop_and_fail; 965 } 966 origcrc = ((unsigned)z.next_in[0] & 0xff) | 967 ((unsigned)z.next_in[1] & 0xff) << 8 | 968 ((unsigned)z.next_in[2] & 0xff) << 16 | 969 ((unsigned)z.next_in[3] & 0xff) << 24; 970 if (origcrc != crc) { 971 maybe_warnx("invalid compressed" 972 " data--crc error"); 973 goto stop_and_fail; 974 } 975 } 976 977 z.avail_in -= 4; 978 z.next_in += 4; 979 980 if (!z.avail_in && done_reading) { 981 goto stop; 982 } 983 state++; 984 break; 985 case GZSTATE_LEN: 986 { 987 uLong origlen; 988 989 if (z.avail_in < 4) { 990 if (!done_reading) { 991 needmore = 1; 992 continue; 993 } 994 maybe_warnx("truncated input"); 995 goto stop_and_fail; 996 } 997 origlen = ((unsigned)z.next_in[0] & 0xff) | 998 ((unsigned)z.next_in[1] & 0xff) << 8 | 999 ((unsigned)z.next_in[2] & 0xff) << 16 | 1000 ((unsigned)z.next_in[3] & 0xff) << 24; 1001 1002 if (origlen != out_sub_tot) { 1003 maybe_warnx("invalid compressed" 1004 " data--length error"); 1005 goto stop_and_fail; 1006 } 1007 } 1008 1009 z.avail_in -= 4; 1010 z.next_in += 4; 1011 1012 if (error < 0) { 1013 maybe_warnx("decompression error"); 1014 goto stop_and_fail; 1015 } 1016 state = GZSTATE_MAGIC0; 1017 break; 1018 } 1019 continue; 1020 stop_and_fail: 1021 out_tot = -1; 1022 stop: 1023 break; 1024 } 1025 if (state > GZSTATE_INIT) 1026 inflateEnd(&z); 1027 1028 free(inbufp); 1029 out1: 1030 free(outbufp); 1031 out2: 1032 if (gsizep) 1033 *gsizep = in_tot; 1034 return (out_tot); 1035 } 1036 1037 #ifndef SMALL 1038 /* 1039 * set the owner, mode, flags & utimes using the given file descriptor. 1040 * file is only used in possible warning messages. 1041 */ 1042 static void 1043 copymodes(int fd, const struct stat *sbp, const char *file) 1044 { 1045 struct timeval times[2]; 1046 struct stat sb; 1047 1048 /* 1049 * If we have no info on the input, give this file some 1050 * default values and return.. 1051 */ 1052 if (sbp == NULL) { 1053 mode_t mask = umask(022); 1054 1055 (void)fchmod(fd, DEFFILEMODE & ~mask); 1056 (void)umask(mask); 1057 return; 1058 } 1059 sb = *sbp; 1060 1061 /* if the chown fails, remove set-id bits as-per compress(1) */ 1062 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { 1063 if (errno != EPERM) 1064 maybe_warn("couldn't fchown: %s", file); 1065 sb.st_mode &= ~(S_ISUID|S_ISGID); 1066 } 1067 1068 /* we only allow set-id and the 9 normal permission bits */ 1069 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 1070 if (fchmod(fd, sb.st_mode) < 0) 1071 maybe_warn("couldn't fchmod: %s", file); 1072 1073 /* only try flags if they exist already */ 1074 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) 1075 maybe_warn("couldn't fchflags: %s", file); 1076 1077 TIMESPEC_TO_TIMEVAL(×[0], &sb.st_atimespec); 1078 TIMESPEC_TO_TIMEVAL(×[1], &sb.st_mtimespec); 1079 if (futimes(fd, times) < 0) 1080 maybe_warn("couldn't utimes: %s", file); 1081 } 1082 #endif 1083 1084 /* what sort of file is this? */ 1085 static enum filetype 1086 file_gettype(u_char *buf) 1087 { 1088 1089 if (buf[0] == GZIP_MAGIC0 && 1090 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 1091 return FT_GZIP; 1092 else 1093 #ifndef NO_BZIP2_SUPPORT 1094 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 1095 buf[3] >= '0' && buf[3] <= '9') 1096 return FT_BZIP2; 1097 else 1098 #endif 1099 #ifndef NO_COMPRESS_SUPPORT 1100 if (memcmp(buf, Z_MAGIC, 2) == 0) 1101 return FT_Z; 1102 else 1103 #endif 1104 return FT_UNKNOWN; 1105 } 1106 1107 #ifndef SMALL 1108 /* check the outfile is OK. */ 1109 static int 1110 check_outfile(const char *outfile) 1111 { 1112 struct stat sb; 1113 int ok = 1; 1114 1115 if (lflag == 0 && stat(outfile, &sb) == 0) { 1116 if (fflag) 1117 unlink(outfile); 1118 else if (isatty(STDIN_FILENO)) { 1119 char ans[10] = { 'n', '\0' }; /* default */ 1120 1121 fprintf(stderr, "%s already exists -- do you wish to " 1122 "overwrite (y or n)? " , outfile); 1123 (void)fgets(ans, sizeof(ans) - 1, stdin); 1124 if (ans[0] != 'y' && ans[0] != 'Y') { 1125 fprintf(stderr, "\tnot overwriting\n"); 1126 ok = 0; 1127 } else 1128 unlink(outfile); 1129 } else { 1130 maybe_warnx("%s already exists -- skipping", outfile); 1131 ok = 0; 1132 } 1133 } 1134 return ok; 1135 } 1136 1137 static void 1138 unlink_input(const char *file, const struct stat *sb) 1139 { 1140 struct stat nsb; 1141 1142 if (stat(file, &nsb) != 0) 1143 /* Must be gone alrady */ 1144 return; 1145 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) 1146 /* Definitely a different file */ 1147 return; 1148 unlink(file); 1149 } 1150 #endif 1151 1152 static const suffixes_t * 1153 check_suffix(char *file, int xlate) 1154 { 1155 const suffixes_t *s; 1156 int len = strlen(file); 1157 char *sp; 1158 1159 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { 1160 /* if it doesn't fit in "a.suf", don't bother */ 1161 if (s->ziplen >= len) 1162 continue; 1163 sp = file + len - s->ziplen; 1164 if (strcmp(s->zipped, sp) != 0) 1165 continue; 1166 if (xlate) 1167 strcpy(sp, s->normal); 1168 return s; 1169 } 1170 return NULL; 1171 } 1172 1173 /* 1174 * compress the given file: create a corresponding .gz file and remove the 1175 * original. 1176 */ 1177 static off_t 1178 file_compress(char *file, char *outfile, size_t outsize) 1179 { 1180 int in; 1181 int out; 1182 off_t size, insize; 1183 #ifndef SMALL 1184 struct stat isb, osb; 1185 const suffixes_t *suff; 1186 #endif 1187 1188 in = open(file, O_RDONLY); 1189 if (in == -1) { 1190 maybe_warn("can't open %s", file); 1191 return -1; 1192 } 1193 1194 if (cflag == 0) { 1195 #ifndef SMALL 1196 if (fstat(in, &isb) == 0) { 1197 if (isb.st_nlink > 1 && fflag == 0) { 1198 maybe_warnx("%s has %d other link%s -- " 1199 "skipping", file, isb.st_nlink - 1, 1200 isb.st_nlink == 1 ? "" : "s"); 1201 close(in); 1202 return -1; 1203 } 1204 } 1205 1206 if (fflag == 0 && (suff = check_suffix(file, 0)) 1207 && suff->zipped[0] != 0) { 1208 maybe_warnx("%s already has %s suffix -- unchanged", 1209 file, suff->zipped); 1210 close(in); 1211 return -1; 1212 } 1213 #endif 1214 1215 /* Add (usually) .gz to filename */ 1216 if ((size_t)snprintf(outfile, outsize, "%s%s", 1217 file, suffixes[0].zipped) >= outsize) 1218 memcpy(outfile - suffixes[0].ziplen - 1, 1219 suffixes[0].zipped, suffixes[0].ziplen + 1); 1220 1221 #ifndef SMALL 1222 if (check_outfile(outfile) == 0) { 1223 close(in); 1224 return -1; 1225 } 1226 #endif 1227 } 1228 1229 if (cflag == 0) { 1230 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); 1231 if (out == -1) { 1232 maybe_warn("could not create output: %s", outfile); 1233 fclose(stdin); 1234 return -1; 1235 } 1236 } else 1237 out = STDOUT_FILENO; 1238 1239 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); 1240 1241 (void)close(in); 1242 1243 /* 1244 * If there was an error, insize will be -1. 1245 * If we compressed to stdout, just return the size. 1246 * Otherwise stat the file and check it is the correct size. 1247 * We only blow away the file if we can stat the output and it 1248 * has the expected size. 1249 */ 1250 if (cflag != 0) 1251 return insize == -1 ? -1 : size; 1252 1253 #ifndef SMALL 1254 if (fstat(out, &osb) != 0) { 1255 maybe_warn("couldn't stat: %s", outfile); 1256 goto bad_outfile; 1257 } 1258 1259 if (osb.st_size != size) { 1260 maybe_warnx("output file: %s wrong size (%" PRIdOFF 1261 " != %" PRIdOFF "), deleting", 1262 outfile, osb.st_size, size); 1263 goto bad_outfile; 1264 } 1265 1266 copymodes(out, &isb, outfile); 1267 #endif 1268 if (close(out) == -1) 1269 maybe_warn("couldn't close output"); 1270 1271 /* output is good, ok to delete input */ 1272 unlink_input(file, &isb); 1273 return size; 1274 1275 #ifndef SMALL 1276 bad_outfile: 1277 if (close(out) == -1) 1278 maybe_warn("couldn't close output"); 1279 1280 maybe_warnx("leaving original %s", file); 1281 unlink(outfile); 1282 return size; 1283 #endif 1284 } 1285 1286 /* uncompress the given file and remove the original */ 1287 static off_t 1288 file_uncompress(char *file, char *outfile, size_t outsize) 1289 { 1290 struct stat isb, osb; 1291 off_t size; 1292 ssize_t rbytes; 1293 unsigned char header1[4]; 1294 enum filetype method; 1295 int rv, fd, ofd, zfd = -1; 1296 #ifndef SMALL 1297 time_t timestamp = 0; 1298 unsigned char name[PATH_MAX + 1]; 1299 #endif 1300 1301 /* gather the old name info */ 1302 1303 fd = open(file, O_RDONLY); 1304 if (fd < 0) { 1305 maybe_warn("can't open %s", file); 1306 goto lose; 1307 } 1308 1309 strlcpy(outfile, file, outsize); 1310 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1311 maybe_warnx("%s: unknown suffix -- ignored", file); 1312 goto lose; 1313 } 1314 1315 rbytes = read(fd, header1, sizeof header1); 1316 if (rbytes != sizeof header1) { 1317 /* we don't want to fail here. */ 1318 #ifndef SMALL 1319 if (fflag) 1320 goto lose; 1321 #endif 1322 if (rbytes == -1) 1323 maybe_warn("can't read %s", file); 1324 else 1325 goto unexpected_EOF; 1326 goto lose; 1327 } 1328 1329 method = file_gettype(header1); 1330 1331 #ifndef SMALL 1332 if (fflag == 0 && method == FT_UNKNOWN) { 1333 maybe_warnx("%s: not in gzip format", file); 1334 goto lose; 1335 } 1336 1337 #endif 1338 1339 #ifndef SMALL 1340 if (method == FT_GZIP && Nflag) { 1341 unsigned char ts[4]; /* timestamp */ 1342 1343 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1344 if (rv >= 0 && (size_t)rv < sizeof ts) 1345 goto unexpected_EOF; 1346 if (rv == -1) { 1347 if (!fflag) 1348 maybe_warn("can't read %s", file); 1349 goto lose; 1350 } 1351 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; 1352 1353 if (header1[3] & ORIG_NAME) { 1354 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME); 1355 if (rbytes < 0) { 1356 maybe_warn("can't read %s", file); 1357 goto lose; 1358 } 1359 if (name[0] != 0) { 1360 /* preserve original directory name */ 1361 char *dp = strrchr(file, '/'); 1362 if (dp == NULL) 1363 dp = file; 1364 else 1365 dp++; 1366 snprintf(outfile, outsize, "%.*s%.*s", 1367 (int) (dp - file), 1368 file, (int) rbytes, name); 1369 } 1370 } 1371 } 1372 #endif 1373 lseek(fd, 0, SEEK_SET); 1374 1375 if (cflag == 0 || lflag) { 1376 if (fstat(fd, &isb) != 0) 1377 goto lose; 1378 #ifndef SMALL 1379 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1380 maybe_warnx("%s has %d other links -- skipping", 1381 file, isb.st_nlink - 1); 1382 goto lose; 1383 } 1384 if (nflag == 0 && timestamp) 1385 isb.st_mtime = timestamp; 1386 if (check_outfile(outfile) == 0) 1387 goto lose; 1388 #endif 1389 } 1390 1391 if (cflag == 0 && lflag == 0) { 1392 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1393 if (zfd == STDOUT_FILENO) { 1394 /* We won't close STDOUT_FILENO later... */ 1395 zfd = dup(zfd); 1396 close(STDOUT_FILENO); 1397 } 1398 if (zfd == -1) { 1399 maybe_warn("can't open %s", outfile); 1400 goto lose; 1401 } 1402 } else 1403 zfd = STDOUT_FILENO; 1404 1405 #ifndef NO_BZIP2_SUPPORT 1406 if (method == FT_BZIP2) { 1407 1408 /* XXX */ 1409 if (lflag) { 1410 maybe_warnx("no -l with bzip2 files"); 1411 goto lose; 1412 } 1413 1414 size = unbzip2(fd, zfd, NULL, 0, NULL); 1415 } else 1416 #endif 1417 1418 #ifndef NO_COMPRESS_SUPPORT 1419 if (method == FT_Z) { 1420 FILE *in, *out; 1421 1422 /* XXX */ 1423 if (lflag) { 1424 maybe_warnx("no -l with Lempel-Ziv files"); 1425 goto lose; 1426 } 1427 1428 if ((in = zdopen(fd)) == NULL) { 1429 maybe_warn("zdopen for read: %s", file); 1430 goto lose; 1431 } 1432 1433 out = fdopen(dup(zfd), "w"); 1434 if (out == NULL) { 1435 maybe_warn("fdopen for write: %s", outfile); 1436 fclose(in); 1437 goto lose; 1438 } 1439 1440 size = zuncompress(in, out, NULL, 0, NULL); 1441 /* need to fclose() if ferror() is true... */ 1442 if (ferror(in) | fclose(in)) { 1443 maybe_warn("failed infile fclose"); 1444 unlink(outfile); 1445 (void)fclose(out); 1446 } 1447 if (fclose(out) != 0) { 1448 maybe_warn("failed outfile fclose"); 1449 unlink(outfile); 1450 goto lose; 1451 } 1452 } else 1453 #endif 1454 1455 #ifndef SMALL 1456 if (method == FT_UNKNOWN) { 1457 if (lflag) { 1458 maybe_warnx("no -l for unknown filetypes"); 1459 goto lose; 1460 } 1461 size = cat_fd(NULL, 0, NULL, fd); 1462 } else 1463 #endif 1464 { 1465 if (lflag) { 1466 print_list(fd, isb.st_size, outfile, isb.st_mtime); 1467 close(fd); 1468 return -1; /* XXX */ 1469 } 1470 1471 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1472 } 1473 1474 if (close(fd) != 0) 1475 maybe_warn("couldn't close input"); 1476 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1477 maybe_warn("couldn't close output"); 1478 1479 if (size == -1) { 1480 if (cflag == 0) 1481 unlink(outfile); 1482 maybe_warnx("%s: uncompress failed", file); 1483 return -1; 1484 } 1485 1486 /* if testing, or we uncompressed to stdout, this is all we need */ 1487 #ifndef SMALL 1488 if (tflag) 1489 return size; 1490 #endif 1491 /* if we are uncompressing to stdin, don't remove the file. */ 1492 if (cflag) 1493 return size; 1494 1495 /* 1496 * if we create a file... 1497 */ 1498 /* 1499 * if we can't stat the file don't remove the file. 1500 */ 1501 1502 ofd = open(outfile, O_RDWR, 0); 1503 if (ofd == -1) { 1504 maybe_warn("couldn't open (leaving original): %s", 1505 outfile); 1506 return -1; 1507 } 1508 if (fstat(ofd, &osb) != 0) { 1509 maybe_warn("couldn't stat (leaving original): %s", 1510 outfile); 1511 close(ofd); 1512 return -1; 1513 } 1514 if (osb.st_size != size) { 1515 maybe_warnx("stat gave different size: %" PRIdOFF 1516 " != %" PRIdOFF " (leaving original)", 1517 size, osb.st_size); 1518 close(ofd); 1519 unlink(outfile); 1520 return -1; 1521 } 1522 unlink_input(file, &isb); 1523 #ifndef SMALL 1524 copymodes(ofd, &isb, outfile); 1525 #endif 1526 close(ofd); 1527 return size; 1528 1529 unexpected_EOF: 1530 maybe_warnx("%s: unexpected end of file", file); 1531 lose: 1532 if (fd != -1) 1533 close(fd); 1534 if (zfd != -1 && zfd != STDOUT_FILENO) 1535 close(fd); 1536 return -1; 1537 } 1538 1539 #ifndef SMALL 1540 static off_t 1541 cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1542 { 1543 char buf[BUFLEN]; 1544 off_t in_tot; 1545 ssize_t w; 1546 1547 in_tot = count; 1548 w = write(STDOUT_FILENO, prepend, count); 1549 if (w == -1 || (size_t)w != count) { 1550 maybe_warn("write to stdout"); 1551 return -1; 1552 } 1553 for (;;) { 1554 ssize_t rv; 1555 1556 rv = read(fd, buf, sizeof buf); 1557 if (rv == 0) 1558 break; 1559 if (rv < 0) { 1560 maybe_warn("read from fd %d", fd); 1561 break; 1562 } 1563 1564 if (write(STDOUT_FILENO, buf, rv) != rv) { 1565 maybe_warn("write to stdout"); 1566 break; 1567 } 1568 in_tot += rv; 1569 } 1570 1571 if (gsizep) 1572 *gsizep = in_tot; 1573 return (in_tot); 1574 } 1575 #endif 1576 1577 static void 1578 handle_stdin(void) 1579 { 1580 unsigned char header1[4]; 1581 off_t usize, gsize; 1582 enum filetype method; 1583 ssize_t bytes_read; 1584 #ifndef NO_COMPRESS_SUPPORT 1585 FILE *in; 1586 #endif 1587 1588 #ifndef SMALL 1589 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1590 maybe_warnx("standard input is a terminal -- ignoring"); 1591 return; 1592 } 1593 #endif 1594 1595 if (lflag) { 1596 struct stat isb; 1597 1598 /* XXX could read the whole file, etc. */ 1599 if (fstat(STDIN_FILENO, &isb) < 0) { 1600 maybe_warn("fstat"); 1601 return; 1602 } 1603 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1604 return; 1605 } 1606 1607 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1); 1608 if (bytes_read == -1) { 1609 maybe_warn("can't read stdin"); 1610 return; 1611 } else if (bytes_read != sizeof(header1)) { 1612 maybe_warnx("(stdin): unexpected end of file"); 1613 return; 1614 } 1615 1616 method = file_gettype(header1); 1617 switch (method) { 1618 default: 1619 #ifndef SMALL 1620 if (fflag == 0) { 1621 maybe_warnx("unknown compression format"); 1622 return; 1623 } 1624 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO); 1625 break; 1626 #endif 1627 case FT_GZIP: 1628 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1629 (char *)header1, sizeof header1, &gsize, "(stdin)"); 1630 break; 1631 #ifndef NO_BZIP2_SUPPORT 1632 case FT_BZIP2: 1633 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1634 (char *)header1, sizeof header1, &gsize); 1635 break; 1636 #endif 1637 #ifndef NO_COMPRESS_SUPPORT 1638 case FT_Z: 1639 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1640 maybe_warnx("zopen of stdin"); 1641 return; 1642 } 1643 1644 usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize); 1645 fclose(in); 1646 break; 1647 #endif 1648 } 1649 1650 #ifndef SMALL 1651 if (vflag && !tflag && usize != -1 && gsize != -1) 1652 print_verbage(NULL, NULL, usize, gsize); 1653 if (vflag && tflag) 1654 print_test("(stdin)", usize != -1); 1655 #endif 1656 1657 } 1658 1659 static void 1660 handle_stdout(void) 1661 { 1662 off_t gsize, usize; 1663 struct stat sb; 1664 time_t systime; 1665 uint32_t mtime; 1666 int ret; 1667 1668 #ifndef SMALL 1669 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1670 maybe_warnx("standard output is a terminal -- ignoring"); 1671 return; 1672 } 1673 #endif 1674 /* If stdin is a file use it's mtime, otherwise use current time */ 1675 ret = fstat(STDIN_FILENO, &sb); 1676 1677 #ifndef SMALL 1678 if (ret < 0) { 1679 maybe_warn("Can't stat stdin"); 1680 return; 1681 } 1682 #endif 1683 1684 if (S_ISREG(sb.st_mode)) 1685 mtime = (uint32_t)sb.st_mtime; 1686 else { 1687 systime = time(NULL); 1688 #ifndef SMALL 1689 if (systime == -1) { 1690 maybe_warn("time"); 1691 return; 1692 } 1693 #endif 1694 mtime = (uint32_t)systime; 1695 } 1696 1697 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1698 #ifndef SMALL 1699 if (vflag && !tflag && usize != -1 && gsize != -1) 1700 print_verbage(NULL, NULL, usize, gsize); 1701 #endif 1702 } 1703 1704 /* do what is asked for, for the path name */ 1705 static void 1706 handle_pathname(char *path) 1707 { 1708 char *opath = path, *s = NULL; 1709 ssize_t len; 1710 int slen; 1711 struct stat sb; 1712 1713 /* check for stdout/stdin */ 1714 if (path[0] == '-' && path[1] == '\0') { 1715 if (dflag) 1716 handle_stdin(); 1717 else 1718 handle_stdout(); 1719 return; 1720 } 1721 1722 retry: 1723 if (stat(path, &sb) != 0) { 1724 /* lets try <path>.gz if we're decompressing */ 1725 if (dflag && s == NULL && errno == ENOENT) { 1726 len = strlen(path); 1727 slen = suffixes[0].ziplen; 1728 s = malloc(len + slen + 1); 1729 if (s == NULL) 1730 maybe_err("malloc"); 1731 memcpy(s, path, len); 1732 memcpy(s + len, suffixes[0].zipped, slen + 1); 1733 path = s; 1734 goto retry; 1735 } 1736 maybe_warn("can't stat: %s", opath); 1737 goto out; 1738 } 1739 1740 if (S_ISDIR(sb.st_mode)) { 1741 #ifndef SMALL 1742 if (rflag) 1743 handle_dir(path); 1744 else 1745 #endif 1746 maybe_warnx("%s is a directory", path); 1747 goto out; 1748 } 1749 1750 if (S_ISREG(sb.st_mode)) 1751 handle_file(path, &sb); 1752 else 1753 maybe_warnx("%s is not a regular file", path); 1754 1755 out: 1756 if (s) 1757 free(s); 1758 } 1759 1760 /* compress/decompress a file */ 1761 static void 1762 handle_file(char *file, struct stat *sbp) 1763 { 1764 off_t usize, gsize; 1765 char outfile[PATH_MAX]; 1766 1767 infile = file; 1768 if (dflag) { 1769 usize = file_uncompress(file, outfile, sizeof(outfile)); 1770 #ifndef SMALL 1771 if (vflag && tflag) 1772 print_test(file, usize != -1); 1773 #endif 1774 if (usize == -1) 1775 return; 1776 gsize = sbp->st_size; 1777 } else { 1778 gsize = file_compress(file, outfile, sizeof(outfile)); 1779 if (gsize == -1) 1780 return; 1781 usize = sbp->st_size; 1782 } 1783 1784 1785 #ifndef SMALL 1786 if (vflag && !tflag) 1787 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1788 #endif 1789 } 1790 1791 #ifndef SMALL 1792 /* this is used with -r to recursively descend directories */ 1793 static void 1794 handle_dir(char *dir) 1795 { 1796 char *path_argv[2]; 1797 FTS *fts; 1798 FTSENT *entry; 1799 1800 path_argv[0] = dir; 1801 path_argv[1] = 0; 1802 fts = fts_open(path_argv, FTS_PHYSICAL, NULL); 1803 if (fts == NULL) { 1804 warn("couldn't fts_open %s", dir); 1805 return; 1806 } 1807 1808 while ((entry = fts_read(fts))) { 1809 switch(entry->fts_info) { 1810 case FTS_D: 1811 case FTS_DP: 1812 continue; 1813 1814 case FTS_DNR: 1815 case FTS_ERR: 1816 case FTS_NS: 1817 maybe_warn("%s", entry->fts_path); 1818 continue; 1819 case FTS_F: 1820 handle_file(entry->fts_name, entry->fts_statp); 1821 } 1822 } 1823 (void)fts_close(fts); 1824 } 1825 #endif 1826 1827 /* print a ratio - size reduction as a fraction of uncompressed size */ 1828 static void 1829 print_ratio(off_t in, off_t out, FILE *where) 1830 { 1831 int percent10; /* 10 * percent */ 1832 off_t diff; 1833 char buff[8]; 1834 int len; 1835 1836 diff = in - out/2; 1837 if (diff <= 0) 1838 /* 1839 * Output is more than double size of input! print -99.9% 1840 * Quite possibly we've failed to get the original size. 1841 */ 1842 percent10 = -999; 1843 else { 1844 /* 1845 * We only need 12 bits of result from the final division, 1846 * so reduce the values until a 32bit division will suffice. 1847 */ 1848 while (in > 0x100000) { 1849 diff >>= 1; 1850 in >>= 1; 1851 } 1852 if (in != 0) 1853 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 1854 else 1855 percent10 = 0; 1856 } 1857 1858 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 1859 /* Move the '.' to before the last digit */ 1860 buff[len - 1] = buff[len - 2]; 1861 buff[len - 2] = '.'; 1862 fprintf(where, "%5s%%", buff); 1863 } 1864 1865 #ifndef SMALL 1866 /* print compression statistics, and the new name (if there is one!) */ 1867 static void 1868 print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 1869 { 1870 if (file) 1871 fprintf(stderr, "%s:%s ", file, 1872 strlen(file) < 7 ? "\t\t" : "\t"); 1873 print_ratio(usize, gsize, stderr); 1874 if (nfile) 1875 fprintf(stderr, " -- replaced with %s", nfile); 1876 fprintf(stderr, "\n"); 1877 fflush(stderr); 1878 } 1879 1880 /* print test results */ 1881 static void 1882 print_test(const char *file, int ok) 1883 { 1884 1885 if (exit_value == 0 && ok == 0) 1886 exit_value = 1; 1887 fprintf(stderr, "%s:%s %s\n", file, 1888 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1889 fflush(stderr); 1890 } 1891 #endif 1892 1893 /* print a file's info ala --list */ 1894 /* eg: 1895 compressed uncompressed ratio uncompressed_name 1896 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1897 */ 1898 static void 1899 print_list(int fd, off_t out, const char *outfile, time_t ts) 1900 { 1901 static int first = 1; 1902 #ifndef SMALL 1903 static off_t in_tot, out_tot; 1904 uint32_t crc = 0; 1905 #endif 1906 off_t in = 0, rv; 1907 1908 if (first) { 1909 #ifndef SMALL 1910 if (vflag) 1911 printf("method crc date time "); 1912 #endif 1913 if (qflag == 0) 1914 printf(" compressed uncompressed " 1915 "ratio uncompressed_name\n"); 1916 } 1917 first = 0; 1918 1919 /* print totals? */ 1920 #ifndef SMALL 1921 if (fd == -1) { 1922 in = in_tot; 1923 out = out_tot; 1924 } else 1925 #endif 1926 { 1927 /* read the last 4 bytes - this is the uncompressed size */ 1928 rv = lseek(fd, (off_t)(-8), SEEK_END); 1929 if (rv != -1) { 1930 unsigned char buf[8]; 1931 uint32_t usize; 1932 1933 rv = read(fd, (char *)buf, sizeof(buf)); 1934 if (rv == -1) 1935 maybe_warn("read of uncompressed size"); 1936 else if (rv != sizeof(buf)) 1937 maybe_warnx("read of uncompressed size"); 1938 1939 else { 1940 usize = buf[4] | buf[5] << 8 | 1941 buf[6] << 16 | buf[7] << 24; 1942 in = (off_t)usize; 1943 #ifndef SMALL 1944 crc = buf[0] | buf[1] << 8 | 1945 buf[2] << 16 | buf[3] << 24; 1946 #endif 1947 } 1948 } 1949 } 1950 1951 #ifndef SMALL 1952 if (vflag && fd == -1) 1953 printf(" "); 1954 else if (vflag) { 1955 char *date = ctime(&ts); 1956 1957 /* skip the day, 1/100th second, and year */ 1958 date += 4; 1959 date[12] = 0; 1960 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 1961 } 1962 in_tot += in; 1963 out_tot += out; 1964 #endif 1965 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 1966 print_ratio(in, out, stdout); 1967 printf(" %s\n", outfile); 1968 } 1969 1970 /* display the usage of NetBSD gzip */ 1971 static void 1972 usage(void) 1973 { 1974 1975 fprintf(stderr, "%s\n", gzip_version); 1976 fprintf(stderr, 1977 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n" 1978 #ifndef SMALL 1979 " -1 --fast fastest (worst) compression\n" 1980 " -2 .. -8 set compression level\n" 1981 " -9 --best best (slowest) compression\n" 1982 " -c --stdout write to stdout, keep original files\n" 1983 " --to-stdout\n" 1984 " -d --decompress uncompress files\n" 1985 " --uncompress\n" 1986 " -f --force force overwriting & compress links\n" 1987 " -h --help display this help\n" 1988 " -l --list list compressed file contents\n" 1989 " -N --name save or restore original file name and time stamp\n" 1990 " -n --no-name don't save original file name or time stamp\n" 1991 " -q --quiet output no warnings\n" 1992 " -r --recursive recursively compress files in directories\n" 1993 " -S .suf use suffix .suf instead of .gz\n" 1994 " --suffix .suf\n" 1995 " -t --test test compressed file\n" 1996 " -V --version display program version\n" 1997 " -v --verbose print extra statistics\n", 1998 #else 1999 , 2000 #endif 2001 getprogname()); 2002 exit(0); 2003 } 2004 2005 #ifndef SMALL 2006 /* display the license information of FreeBSD gzip */ 2007 static void 2008 display_license(void) 2009 { 2010 2011 fprintf(stderr, "%s (based on NetBSD gzip 20060927)\n", gzip_version); 2012 fprintf(stderr, "%s\n", gzip_copyright); 2013 exit(0); 2014 } 2015 #endif 2016 2017 /* display the version of NetBSD gzip */ 2018 static void 2019 display_version(void) 2020 { 2021 2022 fprintf(stderr, "%s\n", gzip_version); 2023 exit(0); 2024 } 2025 2026 #ifndef NO_BZIP2_SUPPORT 2027 #include "unbzip2.c" 2028 #endif 2029 #ifndef NO_COMPRESS_SUPPORT 2030 #include "zuncompress.c" 2031 #endif 2032 2033 static ssize_t 2034 read_retry(int fd, void *buf, size_t sz) 2035 { 2036 char *cp = buf; 2037 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2038 2039 while (left > 0) { 2040 ssize_t ret; 2041 2042 ret = read(fd, cp, left); 2043 if (ret == -1) { 2044 return ret; 2045 } else if (ret == 0) { 2046 break; /* EOF */ 2047 } 2048 cp += ret; 2049 left -= ret; 2050 } 2051 2052 return sz - left; 2053 } 2054 2055