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