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