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