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