gzip.c revision 194508
1206084Srdivacky/* $NetBSD: gzip.c,v 1.92 2008/07/21 14:19:22 lukem Exp $ */ 2198092Srdivacky 3353358Sdim/*- 4353358Sdim * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green 5353358Sdim * All rights reserved. 6198092Srdivacky * 7198092Srdivacky * Redistribution and use in source and binary forms, with or without 8198092Srdivacky * modification, are permitted provided that the following conditions 9206084Srdivacky * are met: 10198092Srdivacky * 1. Redistributions of source code must retain the above copyright 11198092Srdivacky * notice, this list of conditions and the following disclaimer. 12198092Srdivacky * 2. Redistributions in binary form must reproduce the above copyright 13206084Srdivacky * notice, this list of conditions and the following disclaimer in the 14249423Sdim * documentation and/or other materials provided with the distribution. 15249423Sdim * 16198092Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17198092Srdivacky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18218893Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19198092Srdivacky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20198092Srdivacky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21198092Srdivacky * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22344779Sdim * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23249423Sdim * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24249423Sdim * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25249423Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26207619Srdivacky * SUCH DAMAGE. 27276479Sdim * 28207619Srdivacky */ 29198092Srdivacky 30198092Srdivacky#include <sys/cdefs.h> 31198092Srdivacky#ifndef lint 32218893Sdim__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\ 33276479Sdim Matthew R. Green. All rights reserved."); 34276479Sdim__RCSID("$FreeBSD: head/usr.bin/gzip/gzip.c 194508 2009-06-19 19:28:21Z delphij $"); 35276479Sdim#endif /* not lint */ 36276479Sdim 37276479Sdim/* 38276479Sdim * gzip.c -- GPL free gzip using zlib. 39276479Sdim * 40276479Sdim * RFC 1950 covers the zlib format 41276479Sdim * RFC 1951 covers the deflate format 42276479Sdim * RFC 1952 covers the gzip format 43276479Sdim * 44276479Sdim * TODO: 45276479Sdim * - use mmap where possible 46276479Sdim * - handle some signals better (remove outfile?) 47276479Sdim * - make bzip2/compress -v/-t/-l support work as well as possible 48276479Sdim */ 49276479Sdim 50276479Sdim#include <sys/param.h> 51276479Sdim#include <sys/stat.h> 52276479Sdim#include <sys/time.h> 53276479Sdim 54276479Sdim#include <inttypes.h> 55276479Sdim#include <unistd.h> 56276479Sdim#include <stdio.h> 57276479Sdim#include <string.h> 58276479Sdim#include <stdlib.h> 59276479Sdim#include <err.h> 60276479Sdim#include <errno.h> 61276479Sdim#include <fcntl.h> 62276479Sdim#include <zlib.h> 63276479Sdim#include <fts.h> 64341825Sdim#include <libgen.h> 65276479Sdim#include <stdarg.h> 66276479Sdim#include <getopt.h> 67276479Sdim#include <time.h> 68276479Sdim 69276479Sdim#ifndef PRIdOFF 70276479Sdim#define PRIdOFF PRId64 71276479Sdim#endif 72276479Sdim 73276479Sdim/* what type of file are we dealing with */ 74276479Sdimenum filetype { 75276479Sdim FT_GZIP, 76341825Sdim#ifndef NO_BZIP2_SUPPORT 77276479Sdim FT_BZIP2, 78276479Sdim#endif 79276479Sdim#ifndef NO_COMPRESS_SUPPORT 80276479Sdim FT_Z, 81276479Sdim#endif 82276479Sdim FT_LAST, 83276479Sdim FT_UNKNOWN 84276479Sdim}; 85276479Sdim 86276479Sdim#ifndef NO_BZIP2_SUPPORT 87276479Sdim#include <bzlib.h> 88276479Sdim 89276479Sdim#define BZ2_SUFFIX ".bz2" 90276479Sdim#define BZIP2_MAGIC "\102\132\150" 91276479Sdim#endif 92276479Sdim 93276479Sdim#ifndef NO_COMPRESS_SUPPORT 94276479Sdim#define Z_SUFFIX ".Z" 95280031Sdim#define Z_MAGIC "\037\235" 96276479Sdim#endif 97341825Sdim 98276479Sdim#define GZ_SUFFIX ".gz" 99276479Sdim 100276479Sdim#define BUFLEN (64 * 1024) 101288943Sdim 102288943Sdim#define GZIP_MAGIC0 0x1F 103288943Sdim#define GZIP_MAGIC1 0x8B 104288943Sdim#define GZIP_OMAGIC1 0x9E 105288943Sdim 106288943Sdim#define GZIP_TIMESTAMP (off_t)4 107288943Sdim#define GZIP_ORIGNAME (off_t)10 108276479Sdim 109276479Sdim#define HEAD_CRC 0x02 110276479Sdim#define EXTRA_FIELD 0x04 111288943Sdim#define ORIG_NAME 0x08 112288943Sdim#define COMMENT 0x10 113288943Sdim 114288943Sdim#define OS_CODE 3 /* Unix */ 115288943Sdim 116288943Sdimtypedef struct { 117288943Sdim const char *zipped; 118288943Sdim int ziplen; 119288943Sdim const char *normal; /* for unzip - must not be longer than zipped */ 120341825Sdim} suffixes_t; 121276479Sdimstatic suffixes_t suffixes[] = { 122276479Sdim#define SUFFIX(Z, N) {Z, sizeof Z - 1, N} 123309124Sdim SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ 124276479Sdim#ifndef SMALL 125341825Sdim SUFFIX(GZ_SUFFIX, ""), 126276479Sdim SUFFIX(".z", ""), 127276479Sdim SUFFIX("-gz", ""), 128276479Sdim SUFFIX("-z", ""), 129276479Sdim SUFFIX("_z", ""), 130276479Sdim SUFFIX(".taz", ".tar"), 131276479Sdim SUFFIX(".tgz", ".tar"), 132341825Sdim#ifndef NO_BZIP2_SUPPORT 133276479Sdim SUFFIX(BZ2_SUFFIX, ""), 134276479Sdim SUFFIX(".tbz", ".tar"), 135276479Sdim SUFFIX(".tbz2", ".tar"), 136288943Sdim#endif 137288943Sdim#ifndef NO_COMPRESS_SUPPORT 138288943Sdim SUFFIX(Z_SUFFIX, ""), 139276479Sdim#endif 140276479Sdim SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ 141341825Sdim#endif /* SMALL */ 142276479Sdim#undef SUFFIX 143276479Sdim}; 144276479Sdim#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) 145276479Sdim 146276479Sdimstatic const char gzip_version[] = "FreeBSD gzip 20070711"; 147276479Sdim 148276479Sdim#ifndef SMALL 149276479Sdimstatic const char gzip_copyright[] = \ 150276479Sdim" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" 151276479Sdim" All rights reserved.\n" 152276479Sdim"\n" 153276479Sdim" Redistribution and use in source and binary forms, with or without\n" 154276479Sdim" modification, are permitted provided that the following conditions\n" 155288943Sdim" are met:\n" 156276479Sdim" 1. Redistributions of source code must retain the above copyright\n" 157276479Sdim" notice, this list of conditions and the following disclaimer.\n" 158288943Sdim" 2. Redistributions in binary form must reproduce the above copyright\n" 159276479Sdim" notice, this list of conditions and the following disclaimer in the\n" 160276479Sdim" documentation and/or other materials provided with the distribution.\n" 161276479Sdim"\n" 162276479Sdim" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" 163276479Sdim" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" 164276479Sdim" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" 165276479Sdim" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" 166276479Sdim" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" 167276479Sdim" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" 168341825Sdim" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" 169276479Sdim" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" 170341825Sdim" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" 171276479Sdim" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" 172276479Sdim" SUCH DAMAGE."; 173276479Sdim#endif 174276479Sdim 175276479Sdimstatic int cflag; /* stdout mode */ 176276479Sdimstatic int dflag; /* decompress mode */ 177276479Sdimstatic int lflag; /* list mode */ 178276479Sdimstatic int numflag = 6; /* gzip -1..-9 value */ 179341825Sdim 180276479Sdim#ifndef SMALL 181276479Sdimstatic int fflag; /* force mode */ 182276479Sdimstatic int kflag; /* don't delete input files */ 183341825Sdimstatic int nflag; /* don't save name/timestamp */ 184276479Sdimstatic int Nflag; /* don't restore name/timestamp */ 185276479Sdimstatic int qflag; /* quiet mode */ 186341825Sdimstatic int rflag; /* recursive mode */ 187280031Sdimstatic int tflag; /* test */ 188341825Sdimstatic int vflag; /* verbose mode */ 189276479Sdim#else 190341825Sdim#define qflag 0 191276479Sdim#define tflag 0 192276479Sdim#endif 193276479Sdim 194276479Sdimstatic int exit_value = 0; /* exit value */ 195276479Sdim 196276479Sdimstatic char *infile; /* name of file coming in */ 197276479Sdim 198276479Sdimstatic void maybe_err(const char *fmt, ...) __dead2 199276479Sdim __attribute__((__format__(__printf__, 1, 2))); 200276479Sdim#ifndef NO_BZIP2_SUPPORT 201276479Sdimstatic void maybe_errx(const char *fmt, ...) __dead2 202226633Sdim __attribute__((__format__(__printf__, 1, 2))); 203218893Sdim#endif 204218893Sdimstatic void maybe_warn(const char *fmt, ...) 205218893Sdim __attribute__((__format__(__printf__, 1, 2))); 206218893Sdimstatic void maybe_warnx(const char *fmt, ...) 207276479Sdim __attribute__((__format__(__printf__, 1, 2))); 208276479Sdimstatic enum filetype file_gettype(u_char *); 209276479Sdim#ifdef SMALL 210206084Srdivacky#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) 211288943Sdim#endif 212288943Sdimstatic off_t gz_compress(int, int, off_t *, const char *, uint32_t); 213206084Srdivackystatic off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); 214276479Sdimstatic off_t file_compress(char *, char *, size_t); 215206084Srdivackystatic off_t file_uncompress(char *, char *, size_t); 216341825Sdimstatic void handle_pathname(char *); 217341825Sdimstatic void handle_file(char *, struct stat *); 218341825Sdimstatic void handle_stdin(void); 219341825Sdimstatic void handle_stdout(void); 220341825Sdimstatic void print_ratio(off_t, off_t, FILE *); 221341825Sdimstatic void print_list(int fd, off_t, const char *, time_t); 222341825Sdimstatic void usage(void); 223206084Srdivackystatic void display_version(void); 224276479Sdim#ifndef SMALL 225276479Sdimstatic void display_license(void); 226276479Sdim#endif 227276479Sdimstatic const suffixes_t *check_suffix(char *, int); 228276479Sdimstatic ssize_t read_retry(int, void *, size_t); 229276479Sdim 230276479Sdim#ifdef SMALL 231288943Sdim#define unlink_input(f, sb) unlink(f) 232276479Sdim#else 233276479Sdimstatic off_t cat_fd(unsigned char *, size_t, off_t *, int fd); 234249423Sdimstatic void prepend_gzip(char *, int *, char ***); 235249423Sdimstatic void handle_dir(char *); 236249423Sdimstatic void print_verbage(const char *, const char *, off_t, off_t); 237249423Sdimstatic void print_test(const char *, int); 238276479Sdimstatic void copymodes(int fd, const struct stat *, const char *file); 239276479Sdimstatic int check_outfile(const char *outfile); 240249423Sdim#endif 241207619Srdivacky 242276479Sdim#ifndef NO_BZIP2_SUPPORT 243276479Sdimstatic off_t unbzip2(int, int, char *, size_t, off_t *); 244276479Sdim#endif 245276479Sdim 246276479Sdim#ifndef NO_COMPRESS_SUPPORT 247276479Sdimstatic FILE *zdopen(int); 248341825Sdimstatic off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); 249276479Sdim#endif 250276479Sdim 251276479Sdimint main(int, char **p); 252276479Sdim 253276479Sdim#ifdef SMALL 254276479Sdim#define getopt_long(a,b,c,d,e) getopt(a,b,c) 255276479Sdim#else 256276479Sdimstatic const struct option longopts[] = { 257276479Sdim { "stdout", no_argument, 0, 'c' }, 258276479Sdim { "to-stdout", no_argument, 0, 'c' }, 259276479Sdim { "decompress", no_argument, 0, 'd' }, 260276479Sdim { "uncompress", no_argument, 0, 'd' }, 261276479Sdim { "force", no_argument, 0, 'f' }, 262276479Sdim { "help", no_argument, 0, 'h' }, 263276479Sdim { "keep", no_argument, 0, 'k' }, 264276479Sdim { "list", no_argument, 0, 'l' }, 265276479Sdim { "no-name", no_argument, 0, 'n' }, 266276479Sdim { "name", no_argument, 0, 'N' }, 267276479Sdim { "quiet", no_argument, 0, 'q' }, 268276479Sdim { "recursive", no_argument, 0, 'r' }, 269276479Sdim { "suffix", required_argument, 0, 'S' }, 270276479Sdim { "test", no_argument, 0, 't' }, 271276479Sdim { "verbose", no_argument, 0, 'v' }, 272276479Sdim { "version", no_argument, 0, 'V' }, 273276479Sdim { "fast", no_argument, 0, '1' }, 274276479Sdim { "best", no_argument, 0, '9' }, 275249423Sdim { "ascii", no_argument, 0, 'a' }, 276353358Sdim { "license", no_argument, 0, 'L' }, 277276479Sdim { NULL, no_argument, 0, 0 }, 278276479Sdim}; 279280031Sdim#endif 280276479Sdim 281276479Sdimint 282276479Sdimmain(int argc, char **argv) 283276479Sdim{ 284198092Srdivacky const char *progname = getprogname(); 285198092Srdivacky#ifndef SMALL 286276479Sdim char *gzip; 287276479Sdim int len; 288276479Sdim#endif 289280031Sdim int ch; 290276479Sdim 291276479Sdim /* XXX set up signals */ 292276479Sdim 293280031Sdim#ifndef SMALL 294276479Sdim if ((gzip = getenv("GZIP")) != NULL) 295276479Sdim prepend_gzip(gzip, &argc, &argv); 296276479Sdim#endif 297341825Sdim 298276479Sdim /* 299276479Sdim * XXX 300276479Sdim * handle being called `gunzip', `zcat' and `gzcat' 301276479Sdim */ 302276479Sdim if (strcmp(progname, "gunzip") == 0) 303198092Srdivacky dflag = 1; 304276479Sdim else if (strcmp(progname, "zcat") == 0 || 305276479Sdim strcmp(progname, "gzcat") == 0) 306280031Sdim dflag = cflag = 1; 307280031Sdim 308280031Sdim#ifdef SMALL 309280031Sdim#define OPT_LIST "123456789cdhltV" 310280031Sdim#else 311280031Sdim#define OPT_LIST "123456789acdfhklLNnqrS:tVv" 312288943Sdim#endif 313288943Sdim 314288943Sdim while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { 315341825Sdim switch (ch) { 316341825Sdim case '1': case '2': case '3': 317288943Sdim case '4': case '5': case '6': 318280031Sdim case '7': case '8': case '9': 319280031Sdim numflag = ch - '0'; 320280031Sdim break; 321280031Sdim case 'c': 322280031Sdim cflag = 1; 323280031Sdim break; 324280031Sdim case 'd': 325280031Sdim dflag = 1; 326276479Sdim break; 327276479Sdim case 'l': 328276479Sdim lflag = 1; 329276479Sdim dflag = 1; 330276479Sdim break; 331276479Sdim case 'V': 332198092Srdivacky display_version(); 333276479Sdim /* NOTREACHED */ 334276479Sdim#ifndef SMALL 335276479Sdim case 'a': 336276479Sdim fprintf(stderr, "%s: option --ascii ignored on this system\n", progname); 337276479Sdim break; 338276479Sdim case 'f': 339276479Sdim fflag = 1; 340276479Sdim break; 341276479Sdim case 'k': 342276479Sdim kflag = 1; 343276479Sdim break; 344276479Sdim case 'L': 345276479Sdim display_license(); 346198092Srdivacky /* NOT REACHED */ 347276479Sdim case 'N': 348276479Sdim nflag = 0; 349276479Sdim Nflag = 1; 350353358Sdim break; 351276479Sdim case 'n': 352276479Sdim nflag = 1; 353276479Sdim Nflag = 0; 354276479Sdim break; 355276479Sdim case 'q': 356353358Sdim qflag = 1; 357276479Sdim break; 358276479Sdim case 'r': 359276479Sdim rflag = 1; 360276479Sdim break; 361353358Sdim case 'S': 362353358Sdim len = strlen(optarg); 363221345Sdim if (len != 0) { 364353358Sdim suffixes[0].zipped = optarg; 365198092Srdivacky suffixes[0].ziplen = len; 366198092Srdivacky } else { 367276479Sdim suffixes[NUM_SUFFIXES - 1].zipped = ""; 368276479Sdim suffixes[NUM_SUFFIXES - 1].ziplen = 0; 369276479Sdim } 370276479Sdim break; 371276479Sdim case 't': 372276479Sdim cflag = 1; 373276479Sdim tflag = 1; 374276479Sdim dflag = 1; 375276479Sdim break; 376276479Sdim case 'v': 377276479Sdim vflag = 1; 378276479Sdim break; 379276479Sdim#endif 380288943Sdim default: 381276479Sdim usage(); 382276479Sdim /* NOTREACHED */ 383276479Sdim } 384341825Sdim } 385276479Sdim argv += optind; 386276479Sdim argc -= optind; 387276479Sdim 388276479Sdim if (argc == 0) { 389276479Sdim if (dflag) /* stdin mode */ 390276479Sdim handle_stdin(); 391276479Sdim else /* stdout mode */ 392276479Sdim handle_stdout(); 393276479Sdim } else { 394276479Sdim do { 395276479Sdim handle_pathname(argv[0]); 396276479Sdim } while (*++argv); 397276479Sdim } 398276479Sdim#ifndef SMALL 399276479Sdim if (qflag == 0 && lflag && argc > 1) 400276479Sdim print_list(-1, 0, "(totals)", 0); 401276479Sdim#endif 402276479Sdim exit(exit_value); 403276479Sdim} 404276479Sdim 405276479Sdim/* maybe print a warning */ 406207619Srdivackyvoid 407327952Sdimmaybe_warn(const char *fmt, ...) 408341825Sdim{ 409327952Sdim va_list ap; 410327952Sdim 411327952Sdim if (qflag == 0) { 412341825Sdim va_start(ap, fmt); 413341825Sdim vwarn(fmt, ap); 414327952Sdim va_end(ap); 415327952Sdim } 416341825Sdim if (exit_value == 0) 417327952Sdim exit_value = 1; 418341825Sdim} 419341825Sdim 420341825Sdim/* ... without an errno. */ 421341825Sdimvoid 422327952Sdimmaybe_warnx(const char *fmt, ...) 423327952Sdim{ 424327952Sdim va_list ap; 425327952Sdim 426327952Sdim if (qflag == 0) { 427327952Sdim va_start(ap, fmt); 428327952Sdim vwarnx(fmt, ap); 429276479Sdim va_end(ap); 430276479Sdim } 431276479Sdim if (exit_value == 0) 432276479Sdim exit_value = 1; 433276479Sdim} 434276479Sdim 435276479Sdim/* maybe print an error */ 436341825Sdimvoid 437276479Sdimmaybe_err(const char *fmt, ...) 438276479Sdim{ 439276479Sdim va_list ap; 440341825Sdim 441341825Sdim if (qflag == 0) { 442276479Sdim va_start(ap, fmt); 443276479Sdim vwarn(fmt, ap); 444207619Srdivacky va_end(ap); 445234353Sdim } 446327952Sdim exit(2); 447327952Sdim} 448341825Sdim 449341825Sdim#ifndef NO_BZIP2_SUPPORT 450341825Sdim/* ... without an errno. */ 451341825Sdimvoid 452327952Sdimmaybe_errx(const char *fmt, ...) 453327952Sdim{ 454327952Sdim va_list ap; 455327952Sdim 456327952Sdim if (qflag == 0) { 457341825Sdim va_start(ap, fmt); 458341825Sdim vwarnx(fmt, ap); 459341825Sdim va_end(ap); 460341825Sdim } 461276479Sdim exit(2); 462276479Sdim} 463276479Sdim#endif 464198092Srdivacky 465198092Srdivacky#ifndef SMALL 466327952Sdim/* split up $GZIP and prepend it to the argument list */ 467276479Sdimstatic void 468276479Sdimprepend_gzip(char *gzip, int *argc, char ***argv) 469276479Sdim{ 470276479Sdim char *s, **nargv, **ac; 471276479Sdim int nenvarg = 0, i; 472276479Sdim 473276479Sdim /* scan how many arguments there are */ 474276479Sdim for (s = gzip;;) { 475276479Sdim while (*s == ' ' || *s == '\t') 476276479Sdim s++; 477327952Sdim if (*s == 0) 478198092Srdivacky goto count_done; 479276479Sdim nenvarg++; 480198092Srdivacky while (*s != ' ' && *s != '\t') 481276479Sdim if (*s++ == 0) 482276479Sdim goto count_done; 483276479Sdim } 484276479Sdimcount_done: 485276479Sdim /* punt early */ 486276479Sdim if (nenvarg == 0) 487198092Srdivacky return; 488276479Sdim 489276479Sdim *argc += nenvarg; 490276479Sdim ac = *argv; 491276479Sdim 492288943Sdim nargv = (char **)malloc((*argc + 1) * sizeof(char *)); 493288943Sdim if (nargv == NULL) 494288943Sdim maybe_err("malloc"); 495276479Sdim 496288943Sdim /* stash this away */ 497296417Sdim *argv = nargv; 498276479Sdim 499276479Sdim /* copy the program name first */ 500199482Srdivacky i = 0; 501198092Srdivacky nargv[i++] = *(ac++); 502198092Srdivacky 503276479Sdim /* take a copy of $GZIP and add it to the array */ 504276479Sdim s = strdup(gzip); 505276479Sdim if (s == NULL) 506276479Sdim maybe_err("strdup"); 507276479Sdim for (;;) { 508276479Sdim /* Skip whitespaces. */ 509276479Sdim while (*s == ' ' || *s == '\t') 510276479Sdim s++; 511218893Sdim if (*s == 0) 512218893Sdim goto copy_done; 513276479Sdim nargv[i++] = s; 514276479Sdim /* Find the end of this argument. */ 515276479Sdim while (*s != ' ' && *s != '\t') 516276479Sdim if (*s++ == 0) 517276479Sdim /* Argument followed by NUL. */ 518276479Sdim goto copy_done; 519288943Sdim /* Terminate by overwriting ' ' or '\t' with NUL. */ 520276479Sdim *s++ = 0; 521276479Sdim } 522276479Sdimcopy_done: 523276479Sdim 524276479Sdim /* copy the original arguments and a NULL */ 525276479Sdim while (*ac) 526276479Sdim nargv[i++] = *(ac++); 527276479Sdim nargv[i] = NULL; 528276479Sdim} 529276479Sdim#endif 530276479Sdim 531276479Sdim/* compress input to output. Return bytes read, -1 on error */ 532276479Sdimstatic off_t 533276479Sdimgz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) 534276479Sdim{ 535276479Sdim z_stream z; 536276479Sdim char *outbufp, *inbufp; 537276479Sdim off_t in_tot = 0, out_tot = 0; 538276479Sdim ssize_t in_size; 539276479Sdim int i, error; 540288943Sdim uLong crc; 541288943Sdim#ifdef SMALL 542276479Sdim static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, 543276479Sdim 0, 0, 0, 0, 544276479Sdim 0, OS_CODE }; 545276479Sdim#endif 546276479Sdim 547276479Sdim outbufp = malloc(BUFLEN); 548276479Sdim inbufp = malloc(BUFLEN); 549276479Sdim if (outbufp == NULL || inbufp == NULL) { 550276479Sdim maybe_err("malloc failed"); 551276479Sdim goto out; 552276479Sdim } 553208600Srdivacky 554218893Sdim memset(&z, 0, sizeof z); 555208600Srdivacky z.zalloc = Z_NULL; 556276479Sdim z.zfree = Z_NULL; 557276479Sdim z.opaque = 0; 558276479Sdim 559276479Sdim#ifdef SMALL 560234353Sdim memcpy(outbufp, header, sizeof header); 561276479Sdim i = sizeof header; 562276479Sdim#else 563234353Sdim if (nflag != 0) { 564234353Sdim mtime = 0; 565234353Sdim origname = ""; 566234353Sdim } 567276479Sdim 568276479Sdim i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", 569276479Sdim GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 570276479Sdim *origname ? ORIG_NAME : 0, 571276479Sdim mtime & 0xff, 572276479Sdim (mtime >> 8) & 0xff, 573218893Sdim (mtime >> 16) & 0xff, 574276479Sdim (mtime >> 24) & 0xff, 575276479Sdim numflag == 1 ? 4 : numflag == 9 ? 2 : 0, 576276479Sdim OS_CODE, origname); 577276479Sdim if (i >= BUFLEN) 578276479Sdim /* this need PATH_MAX > BUFLEN ... */ 579276479Sdim maybe_err("snprintf"); 580276479Sdim if (*origname) 581276479Sdim i++; 582218893Sdim#endif 583276479Sdim 584276479Sdim z.next_out = (unsigned char *)outbufp + i; 585218893Sdim z.avail_out = BUFLEN - i; 586276479Sdim 587276479Sdim error = deflateInit2(&z, numflag, Z_DEFLATED, 588276479Sdim (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); 589276479Sdim if (error != Z_OK) { 590276479Sdim maybe_warnx("deflateInit2 failed"); 591276479Sdim in_tot = -1; 592276479Sdim goto out; 593276479Sdim } 594218893Sdim 595276479Sdim crc = crc32(0L, Z_NULL, 0); 596353358Sdim for (;;) { 597276479Sdim if (z.avail_out == 0) { 598353358Sdim if (write(out, outbufp, BUFLEN) != BUFLEN) { 599353358Sdim maybe_warn("write"); 600353358Sdim out_tot = -1; 601353358Sdim goto out; 602353358Sdim } 603353358Sdim 604353358Sdim out_tot += BUFLEN; 605353358Sdim z.next_out = (unsigned char *)outbufp; 606353358Sdim z.avail_out = BUFLEN; 607218893Sdim } 608276479Sdim 609276479Sdim if (z.avail_in == 0) { 610276479Sdim in_size = read(in, inbufp, BUFLEN); 611218893Sdim if (in_size < 0) { 612218893Sdim maybe_warn("read"); 613218893Sdim in_tot = -1; 614280031Sdim goto out; 615280031Sdim } 616280031Sdim if (in_size == 0) 617276479Sdim break; 618280031Sdim 619280031Sdim crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); 620280031Sdim in_tot += in_size; 621276479Sdim z.next_in = (unsigned char *)inbufp; 622276479Sdim z.avail_in = in_size; 623276479Sdim } 624276479Sdim 625208600Srdivacky error = deflate(&z, Z_NO_FLUSH); 626276479Sdim if (error != Z_OK && error != Z_STREAM_END) { 627276479Sdim maybe_warnx("deflate failed"); 628276479Sdim in_tot = -1; 629276479Sdim goto out; 630280031Sdim } 631280031Sdim } 632276479Sdim 633208600Srdivacky /* clean up */ 634276479Sdim for (;;) { 635276479Sdim size_t len; 636276479Sdim ssize_t w; 637276479Sdim 638280031Sdim error = deflate(&z, Z_FINISH); 639280031Sdim if (error != Z_OK && error != Z_STREAM_END) { 640280031Sdim maybe_warnx("deflate failed"); 641280031Sdim in_tot = -1; 642280031Sdim goto out; 643341825Sdim } 644276479Sdim 645276479Sdim len = (char *)z.next_out - outbufp; 646201361Srdivacky 647201361Srdivacky w = write(out, outbufp, len); 648276479Sdim if (w == -1 || (size_t)w != len) { 649276479Sdim maybe_warn("write"); 650276479Sdim out_tot = -1; 651276479Sdim goto out; 652276479Sdim } 653276479Sdim out_tot += len; 654276479Sdim z.next_out = (unsigned char *)outbufp; 655276479Sdim z.avail_out = BUFLEN; 656276479Sdim 657276479Sdim if (error == Z_STREAM_END) 658276479Sdim break; 659309124Sdim } 660309124Sdim 661276479Sdim if (deflateEnd(&z) != Z_OK) { 662276479Sdim maybe_warnx("deflateEnd failed"); 663218893Sdim in_tot = -1; 664276479Sdim goto out; 665276479Sdim } 666276479Sdim 667276479Sdim i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 668276479Sdim (int)crc & 0xff, 669276479Sdim (int)(crc >> 8) & 0xff, 670276479Sdim (int)(crc >> 16) & 0xff, 671353358Sdim (int)(crc >> 24) & 0xff, 672218893Sdim (int)in_tot & 0xff, 673218893Sdim (int)(in_tot >> 8) & 0xff, 674276479Sdim (int)(in_tot >> 16) & 0xff, 675276479Sdim (int)(in_tot >> 24) & 0xff); 676276479Sdim if (i != 8) 677276479Sdim maybe_err("snprintf"); 678276479Sdim if (write(out, outbufp, i) != i) { 679276479Sdim maybe_warn("write"); 680276479Sdim in_tot = -1; 681276479Sdim } else 682276479Sdim out_tot += i; 683276479Sdim 684276479Sdimout: 685276479Sdim if (inbufp != NULL) 686276479Sdim free(inbufp); 687276479Sdim if (outbufp != NULL) 688276479Sdim free(outbufp); 689276479Sdim if (gsizep) 690198092Srdivacky *gsizep = out_tot; 691198092Srdivacky return in_tot; 692198092Srdivacky} 693276479Sdim 694276479Sdim/* 695276479Sdim * uncompress input to output then close the input. return the 696276479Sdim * uncompressed size written, and put the compressed sized read 697288943Sdim * into `*gsizep'. 698341825Sdim */ 699276479Sdimstatic off_t 700276479Sdimgz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, 701276479Sdim const char *filename) 702276479Sdim{ 703276479Sdim z_stream z; 704276479Sdim char *outbufp, *inbufp; 705198092Srdivacky off_t out_tot = -1, in_tot = 0; 706276479Sdim uint32_t out_sub_tot = 0; 707198092Srdivacky enum { 708276479Sdim GZSTATE_MAGIC0, 709276479Sdim GZSTATE_MAGIC1, 710276479Sdim GZSTATE_METHOD, 711276479Sdim GZSTATE_FLAGS, 712276479Sdim GZSTATE_SKIPPING, 713276479Sdim GZSTATE_EXTRA, 714276479Sdim GZSTATE_EXTRA2, 715276479Sdim GZSTATE_EXTRA3, 716276479Sdim GZSTATE_ORIGNAME, 717276479Sdim GZSTATE_COMMENT, 718276479Sdim GZSTATE_HEAD_CRC1, 719200583Srdivacky GZSTATE_HEAD_CRC2, 720206084Srdivacky GZSTATE_INIT, 721276479Sdim GZSTATE_READ, 722276479Sdim GZSTATE_CRC, 723276479Sdim GZSTATE_LEN, 724276479Sdim } state = GZSTATE_MAGIC0; 725276479Sdim int flags = 0, skip_count = 0; 726276479Sdim int error = Z_STREAM_ERROR, done_reading = 0; 727198092Srdivacky uLong crc = 0; 728234353Sdim ssize_t wr; 729288943Sdim int needmore = 0; 730198092Srdivacky 731198092Srdivacky#define ADVANCE() { z.next_in++; z.avail_in--; } 732224145Sdim 733224145Sdim if ((outbufp = malloc(BUFLEN)) == NULL) { 734280031Sdim maybe_err("malloc failed"); 735198092Srdivacky goto out2; 736280031Sdim } 737198092Srdivacky if ((inbufp = malloc(BUFLEN)) == NULL) { 738218893Sdim maybe_err("malloc failed"); 739276479Sdim goto out1; 740276479Sdim } 741276479Sdim 742276479Sdim memset(&z, 0, sizeof z); 743280031Sdim z.avail_in = prelen; 744280031Sdim z.next_in = (unsigned char *)pre; 745276479Sdim z.avail_out = BUFLEN; 746276479Sdim z.next_out = (unsigned char *)outbufp; 747276479Sdim z.zalloc = NULL; 748280031Sdim z.zfree = NULL; 749280031Sdim z.opaque = 0; 750280031Sdim 751280031Sdim in_tot = prelen; 752276479Sdim out_tot = 0; 753218893Sdim 754218893Sdim for (;;) { 755276479Sdim if ((z.avail_in == 0 || needmore) && done_reading == 0) { 756276479Sdim ssize_t in_size; 757276479Sdim 758276479Sdim if (z.avail_in > 0) { 759276479Sdim memmove(inbufp, z.next_in, z.avail_in); 760206084Srdivacky } 761218893Sdim z.next_in = (unsigned char *)inbufp; 762276479Sdim in_size = read(in, z.next_in + z.avail_in, 763206084Srdivacky BUFLEN - z.avail_in); 764218893Sdim 765218893Sdim if (in_size == -1) { 766208600Srdivacky maybe_warn("failed to read stdin"); 767198092Srdivacky goto stop_and_fail; 768218893Sdim } else if (in_size == 0) { 769198092Srdivacky done_reading = 1; 770198092Srdivacky } 771218893Sdim 772198092Srdivacky z.avail_in += in_size; 773207619Srdivacky needmore = 0; 774234353Sdim 775261991Sdim in_tot += in_size; 776261991Sdim } 777261991Sdim if (z.avail_in == 0) { 778261991Sdim if (done_reading && state != GZSTATE_MAGIC0) { 779261991Sdim maybe_warnx("%s: unexpected end of file", 780207619Srdivacky filename); 781207619Srdivacky goto stop_and_fail; 782207619Srdivacky } 783207619Srdivacky goto stop; 784218893Sdim } 785218893Sdim switch (state) { 786218893Sdim case GZSTATE_MAGIC0: 787243830Sdim if (*z.next_in != GZIP_MAGIC0) { 788207619Srdivacky if (in_tot > 0) { 789207619Srdivacky maybe_warnx("%s: trailing garbage " 790218893Sdim "ignored", filename); 791218893Sdim goto stop; 792218893Sdim } 793341825Sdim maybe_warnx("input not gziped (MAGIC0)"); 794276479Sdim goto stop_and_fail; 795218893Sdim } 796341825Sdim ADVANCE(); 797243830Sdim state++; 798218893Sdim out_sub_tot = 0; 799218893Sdim crc = crc32(0L, Z_NULL, 0); 800341825Sdim break; 801207619Srdivacky 802341825Sdim case GZSTATE_MAGIC1: 803243830Sdim if (*z.next_in != GZIP_MAGIC1 && 804207619Srdivacky *z.next_in != GZIP_OMAGIC1) { 805207619Srdivacky maybe_warnx("input not gziped (MAGIC1)"); 806207619Srdivacky goto stop_and_fail; 807207619Srdivacky } 808207619Srdivacky ADVANCE(); 809207619Srdivacky state++; 810353358Sdim break; 811353358Sdim 812353358Sdim case GZSTATE_METHOD: 813353358Sdim if (*z.next_in != Z_DEFLATED) { 814207619Srdivacky maybe_warnx("unknown compression method"); 815207619Srdivacky goto stop_and_fail; 816207619Srdivacky } 817207619Srdivacky ADVANCE(); 818207619Srdivacky state++; 819207619Srdivacky break; 820207619Srdivacky 821207619Srdivacky case GZSTATE_FLAGS: 822341825Sdim flags = *z.next_in; 823207619Srdivacky ADVANCE(); 824261991Sdim skip_count = 6; 825207619Srdivacky state++; 826249423Sdim break; 827207619Srdivacky 828249423Sdim case GZSTATE_SKIPPING: 829207619Srdivacky if (skip_count > 0) { 830249423Sdim skip_count--; 831249423Sdim ADVANCE(); 832249423Sdim } else 833249423Sdim state++; 834249423Sdim break; 835249423Sdim 836249423Sdim case GZSTATE_EXTRA: 837249423Sdim if ((flags & EXTRA_FIELD) == 0) { 838249423Sdim state = GZSTATE_ORIGNAME; 839249423Sdim break; 840249423Sdim } 841249423Sdim skip_count = *z.next_in; 842249423Sdim ADVANCE(); 843249423Sdim state++; 844249423Sdim break; 845249423Sdim 846249423Sdim case GZSTATE_EXTRA2: 847249423Sdim skip_count |= ((*z.next_in) << 8); 848249423Sdim ADVANCE(); 849249423Sdim state++; 850249423Sdim break; 851207619Srdivacky 852249423Sdim case GZSTATE_EXTRA3: 853249423Sdim if (skip_count > 0) { 854249423Sdim skip_count--; 855207619Srdivacky ADVANCE(); 856207619Srdivacky } else 857207619Srdivacky state++; 858206084Srdivacky break; 859198092Srdivacky 860207619Srdivacky case GZSTATE_ORIGNAME: 861226633Sdim if ((flags & ORIG_NAME) == 0) { 862207619Srdivacky state++; 863218893Sdim break; 864218893Sdim } 865341825Sdim if (*z.next_in == 0) 866212904Sdim state++; 867207619Srdivacky ADVANCE(); 868207619Srdivacky break; 869207619Srdivacky 870207619Srdivacky case GZSTATE_COMMENT: 871207619Srdivacky if ((flags & COMMENT) == 0) { 872207619Srdivacky state++; 873207619Srdivacky break; 874207619Srdivacky } 875207619Srdivacky if (*z.next_in == 0) 876207619Srdivacky state++; 877207619Srdivacky ADVANCE(); 878207619Srdivacky break; 879207619Srdivacky 880207619Srdivacky case GZSTATE_HEAD_CRC1: 881207619Srdivacky if (flags & HEAD_CRC) 882207619Srdivacky skip_count = 2; 883207619Srdivacky else 884207619Srdivacky skip_count = 0; 885207619Srdivacky state++; 886207619Srdivacky break; 887207619Srdivacky 888207619Srdivacky case GZSTATE_HEAD_CRC2: 889207619Srdivacky if (skip_count > 0) { 890207619Srdivacky skip_count--; 891309124Sdim ADVANCE(); 892207619Srdivacky } else 893207619Srdivacky state++; 894207619Srdivacky break; 895226633Sdim 896249423Sdim case GZSTATE_INIT: 897249423Sdim if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { 898249423Sdim maybe_warnx("failed to inflateInit"); 899249423Sdim goto stop_and_fail; 900249423Sdim } 901288943Sdim state++; 902207619Srdivacky break; 903207619Srdivacky 904309124Sdim case GZSTATE_READ: 905207619Srdivacky error = inflate(&z, Z_FINISH); 906207619Srdivacky switch (error) { 907 /* Z_BUF_ERROR goes with Z_FINISH... */ 908 case Z_BUF_ERROR: 909 case Z_STREAM_END: 910 case Z_OK: 911 break; 912 913 case Z_NEED_DICT: 914 maybe_warnx("Z_NEED_DICT error"); 915 goto stop_and_fail; 916 case Z_DATA_ERROR: 917 maybe_warnx("data stream error"); 918 goto stop_and_fail; 919 case Z_STREAM_ERROR: 920 maybe_warnx("internal stream error"); 921 goto stop_and_fail; 922 case Z_MEM_ERROR: 923 maybe_warnx("memory allocation error"); 924 goto stop_and_fail; 925 926 default: 927 maybe_warn("unknown error from inflate(): %d", 928 error); 929 } 930 wr = BUFLEN - z.avail_out; 931 932 if (wr != 0) { 933 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); 934 if ( 935#ifndef SMALL 936 /* don't write anything with -t */ 937 tflag == 0 && 938#endif 939 write(out, outbufp, wr) != wr) { 940 maybe_warn("error writing to output"); 941 goto stop_and_fail; 942 } 943 944 out_tot += wr; 945 out_sub_tot += wr; 946 } 947 948 if (error == Z_STREAM_END) { 949 inflateEnd(&z); 950 state++; 951 } 952 953 z.next_out = (unsigned char *)outbufp; 954 z.avail_out = BUFLEN; 955 956 break; 957 case GZSTATE_CRC: 958 { 959 uLong origcrc; 960 961 if (z.avail_in < 4) { 962 if (!done_reading) { 963 needmore = 1; 964 continue; 965 } 966 maybe_warnx("truncated input"); 967 goto stop_and_fail; 968 } 969 origcrc = ((unsigned)z.next_in[0] & 0xff) | 970 ((unsigned)z.next_in[1] & 0xff) << 8 | 971 ((unsigned)z.next_in[2] & 0xff) << 16 | 972 ((unsigned)z.next_in[3] & 0xff) << 24; 973 if (origcrc != crc) { 974 maybe_warnx("invalid compressed" 975 " data--crc error"); 976 goto stop_and_fail; 977 } 978 } 979 980 z.avail_in -= 4; 981 z.next_in += 4; 982 983 if (!z.avail_in && done_reading) { 984 goto stop; 985 } 986 state++; 987 break; 988 case GZSTATE_LEN: 989 { 990 uLong origlen; 991 992 if (z.avail_in < 4) { 993 if (!done_reading) { 994 needmore = 1; 995 continue; 996 } 997 maybe_warnx("truncated input"); 998 goto stop_and_fail; 999 } 1000 origlen = ((unsigned)z.next_in[0] & 0xff) | 1001 ((unsigned)z.next_in[1] & 0xff) << 8 | 1002 ((unsigned)z.next_in[2] & 0xff) << 16 | 1003 ((unsigned)z.next_in[3] & 0xff) << 24; 1004 1005 if (origlen != out_sub_tot) { 1006 maybe_warnx("invalid compressed" 1007 " data--length error"); 1008 goto stop_and_fail; 1009 } 1010 } 1011 1012 z.avail_in -= 4; 1013 z.next_in += 4; 1014 1015 if (error < 0) { 1016 maybe_warnx("decompression error"); 1017 goto stop_and_fail; 1018 } 1019 state = GZSTATE_MAGIC0; 1020 break; 1021 } 1022 continue; 1023stop_and_fail: 1024 out_tot = -1; 1025stop: 1026 break; 1027 } 1028 if (state > GZSTATE_INIT) 1029 inflateEnd(&z); 1030 1031 free(inbufp); 1032out1: 1033 free(outbufp); 1034out2: 1035 if (gsizep) 1036 *gsizep = in_tot; 1037 return (out_tot); 1038} 1039 1040#ifndef SMALL 1041/* 1042 * set the owner, mode, flags & utimes using the given file descriptor. 1043 * file is only used in possible warning messages. 1044 */ 1045static void 1046copymodes(int fd, const struct stat *sbp, const char *file) 1047{ 1048 struct timeval times[2]; 1049 struct stat sb; 1050 1051 /* 1052 * If we have no info on the input, give this file some 1053 * default values and return.. 1054 */ 1055 if (sbp == NULL) { 1056 mode_t mask = umask(022); 1057 1058 (void)fchmod(fd, DEFFILEMODE & ~mask); 1059 (void)umask(mask); 1060 return; 1061 } 1062 sb = *sbp; 1063 1064 /* if the chown fails, remove set-id bits as-per compress(1) */ 1065 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { 1066 if (errno != EPERM) 1067 maybe_warn("couldn't fchown: %s", file); 1068 sb.st_mode &= ~(S_ISUID|S_ISGID); 1069 } 1070 1071 /* we only allow set-id and the 9 normal permission bits */ 1072 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; 1073 if (fchmod(fd, sb.st_mode) < 0) 1074 maybe_warn("couldn't fchmod: %s", file); 1075 1076 TIMESPEC_TO_TIMEVAL(×[0], &sb.st_atimespec); 1077 TIMESPEC_TO_TIMEVAL(×[1], &sb.st_mtimespec); 1078 if (futimes(fd, times) < 0) 1079 maybe_warn("couldn't utimes: %s", file); 1080 1081 /* only try flags if they exist already */ 1082 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) 1083 maybe_warn("couldn't fchflags: %s", file); 1084} 1085#endif 1086 1087/* what sort of file is this? */ 1088static enum filetype 1089file_gettype(u_char *buf) 1090{ 1091 1092 if (buf[0] == GZIP_MAGIC0 && 1093 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) 1094 return FT_GZIP; 1095 else 1096#ifndef NO_BZIP2_SUPPORT 1097 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && 1098 buf[3] >= '0' && buf[3] <= '9') 1099 return FT_BZIP2; 1100 else 1101#endif 1102#ifndef NO_COMPRESS_SUPPORT 1103 if (memcmp(buf, Z_MAGIC, 2) == 0) 1104 return FT_Z; 1105 else 1106#endif 1107 return FT_UNKNOWN; 1108} 1109 1110#ifndef SMALL 1111/* check the outfile is OK. */ 1112static int 1113check_outfile(const char *outfile) 1114{ 1115 struct stat sb; 1116 int ok = 1; 1117 1118 if (lflag == 0 && stat(outfile, &sb) == 0) { 1119 if (fflag) 1120 unlink(outfile); 1121 else if (isatty(STDIN_FILENO)) { 1122 char ans[10] = { 'n', '\0' }; /* default */ 1123 1124 fprintf(stderr, "%s already exists -- do you wish to " 1125 "overwrite (y or n)? " , outfile); 1126 (void)fgets(ans, sizeof(ans) - 1, stdin); 1127 if (ans[0] != 'y' && ans[0] != 'Y') { 1128 fprintf(stderr, "\tnot overwriting\n"); 1129 ok = 0; 1130 } else 1131 unlink(outfile); 1132 } else { 1133 maybe_warnx("%s already exists -- skipping", outfile); 1134 ok = 0; 1135 } 1136 } 1137 return ok; 1138} 1139 1140static void 1141unlink_input(const char *file, const struct stat *sb) 1142{ 1143 struct stat nsb; 1144 1145 if (kflag) 1146 return; 1147 if (stat(file, &nsb) != 0) 1148 /* Must be gone alrady */ 1149 return; 1150 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) 1151 /* Definitely a different file */ 1152 return; 1153 unlink(file); 1154} 1155#endif 1156 1157static const suffixes_t * 1158check_suffix(char *file, int xlate) 1159{ 1160 const suffixes_t *s; 1161 int len = strlen(file); 1162 char *sp; 1163 1164 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { 1165 /* if it doesn't fit in "a.suf", don't bother */ 1166 if (s->ziplen >= len) 1167 continue; 1168 sp = file + len - s->ziplen; 1169 if (strcmp(s->zipped, sp) != 0) 1170 continue; 1171 if (xlate) 1172 strcpy(sp, s->normal); 1173 return s; 1174 } 1175 return NULL; 1176} 1177 1178/* 1179 * compress the given file: create a corresponding .gz file and remove the 1180 * original. 1181 */ 1182static off_t 1183file_compress(char *file, char *outfile, size_t outsize) 1184{ 1185 int in; 1186 int out; 1187 off_t size, insize; 1188#ifndef SMALL 1189 struct stat isb, osb; 1190 const suffixes_t *suff; 1191#endif 1192 1193 in = open(file, O_RDONLY); 1194 if (in == -1) { 1195 maybe_warn("can't open %s", file); 1196 return -1; 1197 } 1198 1199 if (cflag == 0) { 1200#ifndef SMALL 1201 if (fstat(in, &isb) == 0) { 1202 if (isb.st_nlink > 1 && fflag == 0) { 1203 maybe_warnx("%s has %d other link%s -- " 1204 "skipping", file, isb.st_nlink - 1, 1205 isb.st_nlink == 1 ? "" : "s"); 1206 close(in); 1207 return -1; 1208 } 1209 } 1210 1211 if (fflag == 0 && (suff = check_suffix(file, 0)) 1212 && suff->zipped[0] != 0) { 1213 maybe_warnx("%s already has %s suffix -- unchanged", 1214 file, suff->zipped); 1215 close(in); 1216 return -1; 1217 } 1218#endif 1219 1220 /* Add (usually) .gz to filename */ 1221 if ((size_t)snprintf(outfile, outsize, "%s%s", 1222 file, suffixes[0].zipped) >= outsize) 1223 memcpy(outfile - suffixes[0].ziplen - 1, 1224 suffixes[0].zipped, suffixes[0].ziplen + 1); 1225 1226#ifndef SMALL 1227 if (check_outfile(outfile) == 0) { 1228 close(in); 1229 return -1; 1230 } 1231#endif 1232 } 1233 1234 if (cflag == 0) { 1235 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); 1236 if (out == -1) { 1237 maybe_warn("could not create output: %s", outfile); 1238 fclose(stdin); 1239 return -1; 1240 } 1241 } else 1242 out = STDOUT_FILENO; 1243 1244 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); 1245 1246 (void)close(in); 1247 1248 /* 1249 * If there was an error, insize will be -1. 1250 * If we compressed to stdout, just return the size. 1251 * Otherwise stat the file and check it is the correct size. 1252 * We only blow away the file if we can stat the output and it 1253 * has the expected size. 1254 */ 1255 if (cflag != 0) 1256 return insize == -1 ? -1 : size; 1257 1258#ifndef SMALL 1259 if (fstat(out, &osb) != 0) { 1260 maybe_warn("couldn't stat: %s", outfile); 1261 goto bad_outfile; 1262 } 1263 1264 if (osb.st_size != size) { 1265 maybe_warnx("output file: %s wrong size (%" PRIdOFF 1266 " != %" PRIdOFF "), deleting", 1267 outfile, osb.st_size, size); 1268 goto bad_outfile; 1269 } 1270 1271 copymodes(out, &isb, outfile); 1272#endif 1273 if (close(out) == -1) 1274 maybe_warn("couldn't close output"); 1275 1276 /* output is good, ok to delete input */ 1277 unlink_input(file, &isb); 1278 return size; 1279 1280#ifndef SMALL 1281 bad_outfile: 1282 if (close(out) == -1) 1283 maybe_warn("couldn't close output"); 1284 1285 maybe_warnx("leaving original %s", file); 1286 unlink(outfile); 1287 return size; 1288#endif 1289} 1290 1291/* uncompress the given file and remove the original */ 1292static off_t 1293file_uncompress(char *file, char *outfile, size_t outsize) 1294{ 1295 struct stat isb, osb; 1296 off_t size; 1297 ssize_t rbytes; 1298 unsigned char header1[4]; 1299 enum filetype method; 1300 int fd, ofd, zfd = -1; 1301#ifndef SMALL 1302 time_t timestamp = 0; 1303 unsigned char name[PATH_MAX + 1]; 1304#endif 1305 1306 /* gather the old name info */ 1307 1308 fd = open(file, O_RDONLY); 1309 if (fd < 0) { 1310 maybe_warn("can't open %s", file); 1311 goto lose; 1312 } 1313 1314 strlcpy(outfile, file, outsize); 1315 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { 1316 maybe_warnx("%s: unknown suffix -- ignored", file); 1317 goto lose; 1318 } 1319 1320 rbytes = read(fd, header1, sizeof header1); 1321 if (rbytes != sizeof header1) { 1322 /* we don't want to fail here. */ 1323#ifndef SMALL 1324 if (fflag) 1325 goto lose; 1326#endif 1327 if (rbytes == -1) 1328 maybe_warn("can't read %s", file); 1329 else 1330 goto unexpected_EOF; 1331 goto lose; 1332 } 1333 1334 method = file_gettype(header1); 1335 1336#ifndef SMALL 1337 if (fflag == 0 && method == FT_UNKNOWN) { 1338 maybe_warnx("%s: not in gzip format", file); 1339 goto lose; 1340 } 1341 1342#endif 1343 1344#ifndef SMALL 1345 if (method == FT_GZIP && Nflag) { 1346 unsigned char ts[4]; /* timestamp */ 1347 int rv; 1348 1349 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); 1350 if (rv >= 0 && (size_t)rv < sizeof ts) 1351 goto unexpected_EOF; 1352 if (rv == -1) { 1353 if (!fflag) 1354 maybe_warn("can't read %s", file); 1355 goto lose; 1356 } 1357 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; 1358 1359 if (header1[3] & ORIG_NAME) { 1360 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME); 1361 if (rbytes < 0) { 1362 maybe_warn("can't read %s", file); 1363 goto lose; 1364 } 1365 if (name[0] != 0) { 1366 /* preserve original directory name */ 1367 char *dp = strrchr(file, '/'); 1368 if (dp == NULL) 1369 dp = file; 1370 else 1371 dp++; 1372 snprintf(outfile, outsize, "%.*s%.*s", 1373 (int) (dp - file), 1374 file, (int) rbytes, name); 1375 } 1376 } 1377 } 1378#endif 1379 lseek(fd, 0, SEEK_SET); 1380 1381 if (cflag == 0 || lflag) { 1382 if (fstat(fd, &isb) != 0) 1383 goto lose; 1384#ifndef SMALL 1385 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { 1386 maybe_warnx("%s has %d other links -- skipping", 1387 file, isb.st_nlink - 1); 1388 goto lose; 1389 } 1390 if (nflag == 0 && timestamp) 1391 isb.st_mtime = timestamp; 1392 if (check_outfile(outfile) == 0) 1393 goto lose; 1394#endif 1395 } 1396 1397 if (cflag == 0 && lflag == 0) { 1398 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); 1399 if (zfd == STDOUT_FILENO) { 1400 /* We won't close STDOUT_FILENO later... */ 1401 zfd = dup(zfd); 1402 close(STDOUT_FILENO); 1403 } 1404 if (zfd == -1) { 1405 maybe_warn("can't open %s", outfile); 1406 goto lose; 1407 } 1408 } else 1409 zfd = STDOUT_FILENO; 1410 1411#ifndef NO_BZIP2_SUPPORT 1412 if (method == FT_BZIP2) { 1413 1414 /* XXX */ 1415 if (lflag) { 1416 maybe_warnx("no -l with bzip2 files"); 1417 goto lose; 1418 } 1419 1420 size = unbzip2(fd, zfd, NULL, 0, NULL); 1421 } else 1422#endif 1423 1424#ifndef NO_COMPRESS_SUPPORT 1425 if (method == FT_Z) { 1426 FILE *in, *out; 1427 1428 /* XXX */ 1429 if (lflag) { 1430 maybe_warnx("no -l with Lempel-Ziv files"); 1431 goto lose; 1432 } 1433 1434 if ((in = zdopen(fd)) == NULL) { 1435 maybe_warn("zdopen for read: %s", file); 1436 goto lose; 1437 } 1438 1439 out = fdopen(dup(zfd), "w"); 1440 if (out == NULL) { 1441 maybe_warn("fdopen for write: %s", outfile); 1442 fclose(in); 1443 goto lose; 1444 } 1445 1446 size = zuncompress(in, out, NULL, 0, NULL); 1447 /* need to fclose() if ferror() is true... */ 1448 if (ferror(in) | fclose(in)) { 1449 maybe_warn("failed infile fclose"); 1450 unlink(outfile); 1451 (void)fclose(out); 1452 } 1453 if (fclose(out) != 0) { 1454 maybe_warn("failed outfile fclose"); 1455 unlink(outfile); 1456 goto lose; 1457 } 1458 } else 1459#endif 1460 1461#ifndef SMALL 1462 if (method == FT_UNKNOWN) { 1463 if (lflag) { 1464 maybe_warnx("no -l for unknown filetypes"); 1465 goto lose; 1466 } 1467 size = cat_fd(NULL, 0, NULL, fd); 1468 } else 1469#endif 1470 { 1471 if (lflag) { 1472 print_list(fd, isb.st_size, outfile, isb.st_mtime); 1473 close(fd); 1474 return -1; /* XXX */ 1475 } 1476 1477 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); 1478 } 1479 1480 if (close(fd) != 0) 1481 maybe_warn("couldn't close input"); 1482 if (zfd != STDOUT_FILENO && close(zfd) != 0) 1483 maybe_warn("couldn't close output"); 1484 1485 if (size == -1) { 1486 if (cflag == 0) 1487 unlink(outfile); 1488 maybe_warnx("%s: uncompress failed", file); 1489 return -1; 1490 } 1491 1492 /* if testing, or we uncompressed to stdout, this is all we need */ 1493#ifndef SMALL 1494 if (tflag) 1495 return size; 1496#endif 1497 /* if we are uncompressing to stdin, don't remove the file. */ 1498 if (cflag) 1499 return size; 1500 1501 /* 1502 * if we create a file... 1503 */ 1504 /* 1505 * if we can't stat the file don't remove the file. 1506 */ 1507 1508 ofd = open(outfile, O_RDWR, 0); 1509 if (ofd == -1) { 1510 maybe_warn("couldn't open (leaving original): %s", 1511 outfile); 1512 return -1; 1513 } 1514 if (fstat(ofd, &osb) != 0) { 1515 maybe_warn("couldn't stat (leaving original): %s", 1516 outfile); 1517 close(ofd); 1518 return -1; 1519 } 1520 if (osb.st_size != size) { 1521 maybe_warnx("stat gave different size: %" PRIdOFF 1522 " != %" PRIdOFF " (leaving original)", 1523 size, osb.st_size); 1524 close(ofd); 1525 unlink(outfile); 1526 return -1; 1527 } 1528 unlink_input(file, &isb); 1529#ifndef SMALL 1530 copymodes(ofd, &isb, outfile); 1531#endif 1532 close(ofd); 1533 return size; 1534 1535 unexpected_EOF: 1536 maybe_warnx("%s: unexpected end of file", file); 1537 lose: 1538 if (fd != -1) 1539 close(fd); 1540 if (zfd != -1 && zfd != STDOUT_FILENO) 1541 close(fd); 1542 return -1; 1543} 1544 1545#ifndef SMALL 1546static off_t 1547cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) 1548{ 1549 char buf[BUFLEN]; 1550 off_t in_tot; 1551 ssize_t w; 1552 1553 in_tot = count; 1554 w = write(STDOUT_FILENO, prepend, count); 1555 if (w == -1 || (size_t)w != count) { 1556 maybe_warn("write to stdout"); 1557 return -1; 1558 } 1559 for (;;) { 1560 ssize_t rv; 1561 1562 rv = read(fd, buf, sizeof buf); 1563 if (rv == 0) 1564 break; 1565 if (rv < 0) { 1566 maybe_warn("read from fd %d", fd); 1567 break; 1568 } 1569 1570 if (write(STDOUT_FILENO, buf, rv) != rv) { 1571 maybe_warn("write to stdout"); 1572 break; 1573 } 1574 in_tot += rv; 1575 } 1576 1577 if (gsizep) 1578 *gsizep = in_tot; 1579 return (in_tot); 1580} 1581#endif 1582 1583static void 1584handle_stdin(void) 1585{ 1586 unsigned char header1[4]; 1587 off_t usize, gsize; 1588 enum filetype method; 1589 ssize_t bytes_read; 1590#ifndef NO_COMPRESS_SUPPORT 1591 FILE *in; 1592#endif 1593 1594#ifndef SMALL 1595 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { 1596 maybe_warnx("standard input is a terminal -- ignoring"); 1597 return; 1598 } 1599#endif 1600 1601 if (lflag) { 1602 struct stat isb; 1603 1604 /* XXX could read the whole file, etc. */ 1605 if (fstat(STDIN_FILENO, &isb) < 0) { 1606 maybe_warn("fstat"); 1607 return; 1608 } 1609 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime); 1610 return; 1611 } 1612 1613 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1); 1614 if (bytes_read == -1) { 1615 maybe_warn("can't read stdin"); 1616 return; 1617 } else if (bytes_read != sizeof(header1)) { 1618 maybe_warnx("(stdin): unexpected end of file"); 1619 return; 1620 } 1621 1622 method = file_gettype(header1); 1623 switch (method) { 1624 default: 1625#ifndef SMALL 1626 if (fflag == 0) { 1627 maybe_warnx("unknown compression format"); 1628 return; 1629 } 1630 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO); 1631 break; 1632#endif 1633 case FT_GZIP: 1634 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 1635 (char *)header1, sizeof header1, &gsize, "(stdin)"); 1636 break; 1637#ifndef NO_BZIP2_SUPPORT 1638 case FT_BZIP2: 1639 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, 1640 (char *)header1, sizeof header1, &gsize); 1641 break; 1642#endif 1643#ifndef NO_COMPRESS_SUPPORT 1644 case FT_Z: 1645 if ((in = zdopen(STDIN_FILENO)) == NULL) { 1646 maybe_warnx("zopen of stdin"); 1647 return; 1648 } 1649 1650 usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize); 1651 fclose(in); 1652 break; 1653#endif 1654 } 1655 1656#ifndef SMALL 1657 if (vflag && !tflag && usize != -1 && gsize != -1) 1658 print_verbage(NULL, NULL, usize, gsize); 1659 if (vflag && tflag) 1660 print_test("(stdin)", usize != -1); 1661#endif 1662 1663} 1664 1665static void 1666handle_stdout(void) 1667{ 1668 off_t gsize, usize; 1669 struct stat sb; 1670 time_t systime; 1671 uint32_t mtime; 1672 int ret; 1673 1674#ifndef SMALL 1675 if (fflag == 0 && isatty(STDOUT_FILENO)) { 1676 maybe_warnx("standard output is a terminal -- ignoring"); 1677 return; 1678 } 1679#endif 1680 /* If stdin is a file use it's mtime, otherwise use current time */ 1681 ret = fstat(STDIN_FILENO, &sb); 1682 1683#ifndef SMALL 1684 if (ret < 0) { 1685 maybe_warn("Can't stat stdin"); 1686 return; 1687 } 1688#endif 1689 1690 if (S_ISREG(sb.st_mode)) 1691 mtime = (uint32_t)sb.st_mtime; 1692 else { 1693 systime = time(NULL); 1694#ifndef SMALL 1695 if (systime == -1) { 1696 maybe_warn("time"); 1697 return; 1698 } 1699#endif 1700 mtime = (uint32_t)systime; 1701 } 1702 1703 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); 1704#ifndef SMALL 1705 if (vflag && !tflag && usize != -1 && gsize != -1) 1706 print_verbage(NULL, NULL, usize, gsize); 1707#endif 1708} 1709 1710/* do what is asked for, for the path name */ 1711static void 1712handle_pathname(char *path) 1713{ 1714 char *opath = path, *s = NULL; 1715 ssize_t len; 1716 int slen; 1717 struct stat sb; 1718 1719 /* check for stdout/stdin */ 1720 if (path[0] == '-' && path[1] == '\0') { 1721 if (dflag) 1722 handle_stdin(); 1723 else 1724 handle_stdout(); 1725 return; 1726 } 1727 1728retry: 1729 if (stat(path, &sb) != 0) { 1730 /* lets try <path>.gz if we're decompressing */ 1731 if (dflag && s == NULL && errno == ENOENT) { 1732 len = strlen(path); 1733 slen = suffixes[0].ziplen; 1734 s = malloc(len + slen + 1); 1735 if (s == NULL) 1736 maybe_err("malloc"); 1737 memcpy(s, path, len); 1738 memcpy(s + len, suffixes[0].zipped, slen + 1); 1739 path = s; 1740 goto retry; 1741 } 1742 maybe_warn("can't stat: %s", opath); 1743 goto out; 1744 } 1745 1746 if (S_ISDIR(sb.st_mode)) { 1747#ifndef SMALL 1748 if (rflag) 1749 handle_dir(path); 1750 else 1751#endif 1752 maybe_warnx("%s is a directory", path); 1753 goto out; 1754 } 1755 1756 if (S_ISREG(sb.st_mode)) 1757 handle_file(path, &sb); 1758 else 1759 maybe_warnx("%s is not a regular file", path); 1760 1761out: 1762 if (s) 1763 free(s); 1764} 1765 1766/* compress/decompress a file */ 1767static void 1768handle_file(char *file, struct stat *sbp) 1769{ 1770 off_t usize, gsize; 1771 char outfile[PATH_MAX]; 1772 1773 infile = file; 1774 if (dflag) { 1775 usize = file_uncompress(file, outfile, sizeof(outfile)); 1776#ifndef SMALL 1777 if (vflag && tflag) 1778 print_test(file, usize != -1); 1779#endif 1780 if (usize == -1) 1781 return; 1782 gsize = sbp->st_size; 1783 } else { 1784 gsize = file_compress(file, outfile, sizeof(outfile)); 1785 if (gsize == -1) 1786 return; 1787 usize = sbp->st_size; 1788 } 1789 1790 1791#ifndef SMALL 1792 if (vflag && !tflag) 1793 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); 1794#endif 1795} 1796 1797#ifndef SMALL 1798/* this is used with -r to recursively descend directories */ 1799static void 1800handle_dir(char *dir) 1801{ 1802 char *path_argv[2]; 1803 FTS *fts; 1804 FTSENT *entry; 1805 1806 path_argv[0] = dir; 1807 path_argv[1] = 0; 1808 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 1809 if (fts == NULL) { 1810 warn("couldn't fts_open %s", dir); 1811 return; 1812 } 1813 1814 while ((entry = fts_read(fts))) { 1815 switch(entry->fts_info) { 1816 case FTS_D: 1817 case FTS_DP: 1818 continue; 1819 1820 case FTS_DNR: 1821 case FTS_ERR: 1822 case FTS_NS: 1823 maybe_warn("%s", entry->fts_path); 1824 continue; 1825 case FTS_F: 1826 handle_file(entry->fts_path, entry->fts_statp); 1827 } 1828 } 1829 (void)fts_close(fts); 1830} 1831#endif 1832 1833/* print a ratio - size reduction as a fraction of uncompressed size */ 1834static void 1835print_ratio(off_t in, off_t out, FILE *where) 1836{ 1837 int percent10; /* 10 * percent */ 1838 off_t diff; 1839 char buff[8]; 1840 int len; 1841 1842 diff = in - out/2; 1843 if (diff <= 0) 1844 /* 1845 * Output is more than double size of input! print -99.9% 1846 * Quite possibly we've failed to get the original size. 1847 */ 1848 percent10 = -999; 1849 else { 1850 /* 1851 * We only need 12 bits of result from the final division, 1852 * so reduce the values until a 32bit division will suffice. 1853 */ 1854 while (in > 0x100000) { 1855 diff >>= 1; 1856 in >>= 1; 1857 } 1858 if (in != 0) 1859 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; 1860 else 1861 percent10 = 0; 1862 } 1863 1864 len = snprintf(buff, sizeof buff, "%2.2d.", percent10); 1865 /* Move the '.' to before the last digit */ 1866 buff[len - 1] = buff[len - 2]; 1867 buff[len - 2] = '.'; 1868 fprintf(where, "%5s%%", buff); 1869} 1870 1871#ifndef SMALL 1872/* print compression statistics, and the new name (if there is one!) */ 1873static void 1874print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) 1875{ 1876 if (file) 1877 fprintf(stderr, "%s:%s ", file, 1878 strlen(file) < 7 ? "\t\t" : "\t"); 1879 print_ratio(usize, gsize, stderr); 1880 if (nfile) 1881 fprintf(stderr, " -- replaced with %s", nfile); 1882 fprintf(stderr, "\n"); 1883 fflush(stderr); 1884} 1885 1886/* print test results */ 1887static void 1888print_test(const char *file, int ok) 1889{ 1890 1891 if (exit_value == 0 && ok == 0) 1892 exit_value = 1; 1893 fprintf(stderr, "%s:%s %s\n", file, 1894 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); 1895 fflush(stderr); 1896} 1897#endif 1898 1899/* print a file's info ala --list */ 1900/* eg: 1901 compressed uncompressed ratio uncompressed_name 1902 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar 1903*/ 1904static void 1905print_list(int fd, off_t out, const char *outfile, time_t ts) 1906{ 1907 static int first = 1; 1908#ifndef SMALL 1909 static off_t in_tot, out_tot; 1910 uint32_t crc = 0; 1911#endif 1912 off_t in = 0, rv; 1913 1914 if (first) { 1915#ifndef SMALL 1916 if (vflag) 1917 printf("method crc date time "); 1918#endif 1919 if (qflag == 0) 1920 printf(" compressed uncompressed " 1921 "ratio uncompressed_name\n"); 1922 } 1923 first = 0; 1924 1925 /* print totals? */ 1926#ifndef SMALL 1927 if (fd == -1) { 1928 in = in_tot; 1929 out = out_tot; 1930 } else 1931#endif 1932 { 1933 /* read the last 4 bytes - this is the uncompressed size */ 1934 rv = lseek(fd, (off_t)(-8), SEEK_END); 1935 if (rv != -1) { 1936 unsigned char buf[8]; 1937 uint32_t usize; 1938 1939 rv = read(fd, (char *)buf, sizeof(buf)); 1940 if (rv == -1) 1941 maybe_warn("read of uncompressed size"); 1942 else if (rv != sizeof(buf)) 1943 maybe_warnx("read of uncompressed size"); 1944 1945 else { 1946 usize = buf[4] | buf[5] << 8 | 1947 buf[6] << 16 | buf[7] << 24; 1948 in = (off_t)usize; 1949#ifndef SMALL 1950 crc = buf[0] | buf[1] << 8 | 1951 buf[2] << 16 | buf[3] << 24; 1952#endif 1953 } 1954 } 1955 } 1956 1957#ifndef SMALL 1958 if (vflag && fd == -1) 1959 printf(" "); 1960 else if (vflag) { 1961 char *date = ctime(&ts); 1962 1963 /* skip the day, 1/100th second, and year */ 1964 date += 4; 1965 date[12] = 0; 1966 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); 1967 } 1968 in_tot += in; 1969 out_tot += out; 1970#else 1971 (void)&ts; /* XXX */ 1972#endif 1973 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); 1974 print_ratio(in, out, stdout); 1975 printf(" %s\n", outfile); 1976} 1977 1978/* display the usage of NetBSD gzip */ 1979static void 1980usage(void) 1981{ 1982 1983 fprintf(stderr, "%s\n", gzip_version); 1984 fprintf(stderr, 1985#ifdef SMALL 1986 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n", 1987#else 1988 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n" 1989 " -1 --fast fastest (worst) compression\n" 1990 " -2 .. -8 set compression level\n" 1991 " -9 --best best (slowest) compression\n" 1992 " -c --stdout write to stdout, keep original files\n" 1993 " --to-stdout\n" 1994 " -d --decompress uncompress files\n" 1995 " --uncompress\n" 1996 " -f --force force overwriting & compress links\n" 1997 " -h --help display this help\n" 1998 " -k --keep don't delete input files during operation\n" 1999 " -l --list list compressed file contents\n" 2000 " -N --name save or restore original file name and time stamp\n" 2001 " -n --no-name don't save original file name or time stamp\n" 2002 " -q --quiet output no warnings\n" 2003 " -r --recursive recursively compress files in directories\n" 2004 " -S .suf use suffix .suf instead of .gz\n" 2005 " --suffix .suf\n" 2006 " -t --test test compressed file\n" 2007 " -V --version display program version\n" 2008 " -v --verbose print extra statistics\n", 2009#endif 2010 getprogname()); 2011 exit(0); 2012} 2013 2014#ifndef SMALL 2015/* display the license information of FreeBSD gzip */ 2016static void 2017display_license(void) 2018{ 2019 2020 fprintf(stderr, "%s (based on NetBSD gzip 20060927)\n", gzip_version); 2021 fprintf(stderr, "%s\n", gzip_copyright); 2022 exit(0); 2023} 2024#endif 2025 2026/* display the version of NetBSD gzip */ 2027static void 2028display_version(void) 2029{ 2030 2031 fprintf(stderr, "%s\n", gzip_version); 2032 exit(0); 2033} 2034 2035#ifndef NO_BZIP2_SUPPORT 2036#include "unbzip2.c" 2037#endif 2038#ifndef NO_COMPRESS_SUPPORT 2039#include "zuncompress.c" 2040#endif 2041 2042static ssize_t 2043read_retry(int fd, void *buf, size_t sz) 2044{ 2045 char *cp = buf; 2046 size_t left = MIN(sz, (size_t) SSIZE_MAX); 2047 2048 while (left > 0) { 2049 ssize_t ret; 2050 2051 ret = read(fd, cp, left); 2052 if (ret == -1) { 2053 return ret; 2054 } else if (ret == 0) { 2055 break; /* EOF */ 2056 } 2057 cp += ret; 2058 left -= ret; 2059 } 2060 2061 return sz - left; 2062} 2063 2064