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