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