Annotation of src/usr.bin/compress/main.c, Revision 1.69
1.69 ! millert 1: /* $OpenBSD: main.c,v 1.68 2007/01/18 20:53:36 millert 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.69 ! millert 39: static const char main_rcsid[] = "$OpenBSD: main.c,v 1.68 2007/01/18 20:53:36 millert 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.63 otto 73: int (*close)(void *, struct z_info *, const char *, struct stat *);
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.31 millert 98: __dead void usage(int);
1.49 henning 99: int docompress(const char *, char *, const struct compressor *,
1.25 deraadt 100: int, struct stat *);
1.49 henning 101: int dodecompress(const char *, char *, const struct compressor *,
1.25 deraadt 102: int, struct stat *);
1.37 millert 103: const struct compressor *check_method(int);
1.36 millert 104: const char *check_suffix(const char *);
105: char *set_outfile(const char *, char *, size_t);
1.37 millert 106: void list_stats(const char *, const struct compressor *, struct z_info *);
107: void verbose_info(const char *, off_t, off_t, u_int32_t);
1.18 mickey 108:
1.19 millert 109: #define OPTSTRING "123456789ab:cdfghlLnNOo:qrS:tvV"
1.18 mickey 110: const struct option longopts[] = {
1.45 tedu 111: #ifndef SMALL
1.18 mickey 112: { "ascii", no_argument, 0, 'a' },
113: { "stdout", no_argument, 0, 'c' },
114: { "to-stdout", no_argument, 0, 'c' },
115: { "decompress", no_argument, 0, 'd' },
116: { "uncompress", no_argument, 0, 'd' },
117: { "force", no_argument, 0, 'f' },
118: { "help", no_argument, 0, 'h' },
119: { "list", no_argument, 0, 'l' },
120: { "license", no_argument, 0, 'L' },
121: { "no-name", no_argument, 0, 'n' },
122: { "name", no_argument, 0, 'N' },
123: { "quiet", no_argument, 0, 'q' },
124: { "recursive", no_argument, 0, 'r' },
125: { "suffix", required_argument, 0, 'S' },
126: { "test", no_argument, 0, 't' },
127: { "verbose", no_argument, 0, 'v' },
128: { "version", no_argument, 0, 'V' },
129: { "fast", no_argument, 0, '1' },
130: { "best", no_argument, 0, '9' },
1.45 tedu 131: #endif /* SMALL */
1.18 mickey 132: { NULL }
133: };
1.1 mickey 134:
135: int
1.24 deraadt 136: main(int argc, char *argv[])
1.1 mickey 137: {
1.19 millert 138: FTS *ftsp;
139: FTSENT *entry;
1.18 mickey 140: const struct compressor *method;
1.36 millert 141: const char *s;
142: char *p, *infile;
1.19 millert 143: char outfile[MAXPATHLEN], _infile[MAXPATHLEN], suffix[16];
1.18 mickey 144: char *nargv[512]; /* some estimate based on ARG_MAX */
1.66 millert 145: int bits, ch, error, i, rc, cflag, oflag;
1.1 mickey 146:
1.66 millert 147: bits = cflag = oflag = 0;
1.38 millert 148: nosave = -1;
1.1 mickey 149: p = __progname;
150: if (p[0] == 'g') {
151: method = M_DEFLATE;
1.18 mickey 152: bits = 6;
1.1 mickey 153: p++;
154: } else
1.45 tedu 155: #ifdef SMALL
156: method = M_DEFLATE;
157: #else
1.1 mickey 158: method = M_COMPRESS;
1.45 tedu 159: #endif /* SMALL */
1.1 mickey 160:
161: decomp = 0;
162: if (!strcmp(p, "zcat")) {
163: decomp++;
1.66 millert 164: cflag = 1;
1.1 mickey 165: } else {
166: if (p[0] == 'u' && p[1] == 'n') {
167: p += 2;
168: decomp++;
169: }
170:
1.2 mickey 171: if (strcmp(p, "zip") &&
172: strcmp(p, "compress"))
1.1 mickey 173: errx(1, "unknown program name");
174: }
175:
1.18 mickey 176: strlcpy(suffix, method->suffix, sizeof(suffix));
177:
1.19 millert 178: nargv[0] = NULL;
1.68 millert 179: if (method == M_DEFLATE && (p = getenv("GZIP")) != NULL) {
1.18 mickey 180: char *last;
181:
182: nargv[0] = *argv++;
1.36 millert 183: for (i = 1, (p = strtok_r(p, " ", &last)); p != NULL;
1.18 mickey 184: (p = strtok_r(NULL, " ", &last)), i++)
185: if (i < sizeof(nargv)/sizeof(nargv[1]) - argc - 1)
186: nargv[i] = p;
1.62 deraadt 187: else
1.18 mickey 188: errx(1, "GZIP is too long");
189: argc += i - 1;
190: while ((nargv[i++] = *argv++))
191: ;
192: argv = nargv;
193: }
194:
195: while ((ch = getopt_long(argc, argv, OPTSTRING, longopts, NULL)) != -1)
1.1 mickey 196: switch(ch) {
197: case '1':
198: case '2':
199: case '3':
200: case '4':
201: case '5':
202: case '6':
203: case '7':
204: case '8':
205: case '9':
206: method = M_DEFLATE;
1.18 mickey 207: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 208: bits = ch - '0';
209: break;
1.18 mickey 210: case 'a':
211: warnx("option -a is ignored on this system");
212: break;
1.1 mickey 213: case 'b':
214: bits = strtol(optarg, &p, 10);
1.7 denny 215: /*
216: * POSIX 1002.3 says 9 <= bits <= 14 for portable
217: * apps, but says the implementation may allow
218: * greater.
219: */
1.1 mickey 220: if (*p)
221: errx(1, "illegal bit count -- %s", optarg);
222: break;
223: case 'c':
1.66 millert 224: cflag = 1;
1.1 mickey 225: break;
226: case 'd': /* Backward compatible. */
227: decomp++;
228: break;
229: case 'f':
230: force++;
231: break;
232: case 'g':
233: method = M_DEFLATE;
1.18 mickey 234: strlcpy(suffix, method->suffix, sizeof(suffix));
235: bits = 6;
1.1 mickey 236: break;
237: case 'l':
238: list++;
1.37 millert 239: testmode++;
240: decomp++;
1.1 mickey 241: break;
242: case 'n':
1.37 millert 243: nosave = 1;
1.1 mickey 244: break;
245: case 'N':
1.38 millert 246: nosave = 0;
1.1 mickey 247: break;
1.45 tedu 248: #ifndef SMALL
1.1 mickey 249: case 'O':
250: method = M_COMPRESS;
1.18 mickey 251: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 252: break;
1.45 tedu 253: #endif /* SMALL */
1.1 mickey 254: case 'o':
1.18 mickey 255: if (strlcpy(outfile, optarg,
256: sizeof(outfile)) >= sizeof(outfile))
257: errx(1, "-o argument is too long");
1.20 mickey 258: oflag = 1;
1.1 mickey 259: break;
260: case 'q':
261: verbose = -1;
262: break;
263: case 'S':
264: p = suffix;
265: if (optarg[0] != '.')
266: *p++ = '.';
1.18 mickey 267: strlcpy(p, optarg, sizeof(suffix) - (p - suffix));
268: p = optarg;
1.1 mickey 269: break;
270: case 't':
1.20 mickey 271: testmode = 1;
1.29 millert 272: decomp++;
1.1 mickey 273: break;
1.51 henning 274: #ifndef SMALL
1.18 mickey 275: case 'V':
1.48 millert 276: printf("%s\n%s\n", main_rcsid, gz_rcsid);
277: printf("%s\n%s\n", z_rcsid, null_rcsid);
1.45 tedu 278: #endif
1.19 millert 279: exit (0);
1.1 mickey 280: case 'v':
281: verbose++;
282: break;
1.52 henning 283: #ifndef SMALL
1.18 mickey 284: case 'L':
285: fputs(copyright, stderr);
286: fputs(license, stderr);
1.45 tedu 287: #endif
1.19 millert 288: exit (0);
1.18 mickey 289: case 'r':
1.19 millert 290: recurse++;
1.18 mickey 291: break;
292:
1.1 mickey 293: case 'h':
1.31 millert 294: usage(0);
295: break;
1.1 mickey 296: default:
1.31 millert 297: usage(1);
1.1 mickey 298: }
299: argc -= optind;
300: argv += optind;
301:
1.19 millert 302: if (argc == 0) {
303: if (nargv[0] == NULL)
304: argv = nargv;
305: /* XXX - make sure we don't oflow nargv in $GZIP case (millert) */
1.66 millert 306: argv[0] = "-";
1.19 millert 307: argv[1] = NULL;
308: }
309: if (oflag && (recurse || argc > 1))
310: errx(1, "-o option may only be used with a single input file");
1.20 mickey 311:
1.34 mickey 312: if ((cat && argc) + testmode + oflag > 1)
1.19 millert 313: errx(1, "may not mix -o, -c, or -t options");
1.38 millert 314: if (nosave == -1)
315: nosave = decomp;
1.19 millert 316:
317: if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL)
318: err(1, NULL);
1.37 millert 319: for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) {
1.66 millert 320: cat = cflag;
321: pipin = 0;
1.19 millert 322: infile = entry->fts_path;
1.67 otto 323: if (infile[0] == '-' && infile[1] == '\0') {
324: infile = "stdin";
325: pipin++;
326: if (!oflag)
327: cat = 1;
328: }
329: else
330: switch (entry->fts_info) {
331: case FTS_D:
332: if (!recurse) {
333: warnx("%s is a directory: ignored",
334: infile);
335: fts_set(ftsp, entry, FTS_SKIP);
1.66 millert 336: }
1.67 otto 337: continue;
338: case FTS_DP:
339: continue;
340: case FTS_NS:
341: /*
342: * If file does not exist and has no suffix,
343: * tack on the default suffix and try that.
344: */
345: if (entry->fts_errno == ENOENT) {
346: p = strrchr(entry->fts_accpath, '.');
347: if ((p == NULL ||
348: strcmp(p, suffix) != 0) &&
349: snprintf(_infile, sizeof(_infile),
350: "%s%s", infile, suffix) <
351: sizeof(_infile) &&
352: stat(_infile, entry->fts_statp) ==
353: 0 &&
354: S_ISREG(entry->fts_statp->st_mode)) {
355: infile = _infile;
356: break;
357: }
1.66 millert 358: }
1.67 otto 359: case FTS_ERR:
360: case FTS_DNR:
361: warnx("%s: %s", infile,
362: strerror(entry->fts_errno));
1.37 millert 363: rc = rc ? rc : WARNING;
1.19 millert 364: continue;
1.67 otto 365: default:
366: if (!S_ISREG(entry->fts_statp->st_mode) &&
367: !(S_ISLNK(entry->fts_statp->st_mode) &&
368: cat)) {
369: warnx("%s not a regular file%s",
370: infile, cat ? "" : ": unchanged");
371: rc = rc ? rc : WARNING;
372: continue;
373: }
374: break;
1.18 mickey 375: }
1.1 mickey 376:
1.36 millert 377: if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) {
378: warnx("%s already has %s suffix -- unchanged",
379: infile, s);
1.37 millert 380: rc = rc ? rc : WARNING;
1.36 millert 381: continue;
382: }
383:
1.66 millert 384: if (!oflag) {
385: if (cat)
386: strlcpy(outfile, "stdout", sizeof(outfile));
387: else if (decomp) {
1.35 millert 388: if (set_outfile(infile, outfile,
389: sizeof outfile) == NULL) {
1.53 millert 390: if (!recurse) {
1.19 millert 391: warnx("%s: unknown suffix: "
392: "ignored", infile);
1.53 millert 393: rc = rc ? rc : WARNING;
394: }
1.19 millert 395: continue;
396: }
397: } else {
398: if (snprintf(outfile, sizeof(outfile),
399: "%s%s", infile, suffix) >= sizeof(outfile)) {
400: warnx("%s%s: name too long",
401: infile, suffix);
1.53 millert 402: rc = rc ? rc : WARNING;
1.19 millert 403: continue;
404: }
405: }
1.1 mickey 406: }
407:
1.37 millert 408: if (verbose > 0 && !pipin && !list)
1.1 mickey 409: fprintf(stderr, "%s:\t", infile);
410:
1.49 henning 411: error = (decomp ? dodecompress : docompress)
1.60 deraadt 412: (infile, outfile, method, bits, entry->fts_statp);
1.1 mickey 413:
1.37 millert 414: switch (error) {
415: case SUCCESS:
416: if (!cat && !testmode) {
417: if (!pipin && unlink(infile) && verbose >= 0)
1.18 mickey 418: warn("input: %s", infile);
1.1 mickey 419: }
1.37 millert 420: break;
421: case WARNING:
422: rc = rc ? rc : WARNING;
423: break;
424: default:
425: rc = FAILURE;
426: break;
1.1 mickey 427: }
1.19 millert 428: }
1.37 millert 429: if (list)
430: list_stats(NULL, NULL, NULL);
1.1 mickey 431:
1.18 mickey 432: exit(rc);
1.1 mickey 433: }
434:
435: int
1.49 henning 436: docompress(const char *in, char *out, const struct compressor *method,
1.24 deraadt 437: int bits, struct stat *sb)
1.1 mickey 438: {
1.59 moritz 439: #ifndef SMALL
1.18 mickey 440: u_char buf[Z_BUFSIZE];
1.37 millert 441: char *name;
1.66 millert 442: int error, ifd, ofd, flags, oreg;
1.16 mpech 443: void *cookie;
444: ssize_t nr;
1.37 millert 445: u_int32_t mtime;
446: struct z_info info;
1.66 millert 447: struct stat osb;
1.1 mickey 448:
1.37 millert 449: mtime = 0;
1.66 millert 450: flags = oreg = 0;
1.37 millert 451: error = SUCCESS;
452: name = NULL;
1.1 mickey 453: cookie = NULL;
1.3 mickey 454:
1.66 millert 455: if (pipin)
456: ifd = dup(STDIN_FILENO);
457: else
458: ifd = open(in, O_RDONLY);
459: if (ifd < 0) {
1.21 millert 460: if (verbose >= 0)
1.64 millert 461: warn("%s", in);
1.37 millert 462: return (FAILURE);
1.21 millert 463: }
464:
1.66 millert 465: if (cat)
466: ofd = dup(STDOUT_FILENO);
467: else {
468: if (stat(out, &osb) == 0) {
469: oreg = S_ISREG(osb.st_mode);
470: if (!force && oreg && !permission(out)) {
471: (void) close(ifd);
472: return (WARNING);
473: }
474: }
475: ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR);
476: }
477: if (ofd < 0) {
1.3 mickey 478: if (verbose >= 0)
479: warn("%s", out);
1.32 mickey 480: (void) close(ifd);
1.37 millert 481: return (FAILURE);
1.3 mickey 482: }
483:
1.4 mickey 484: if (method != M_COMPRESS && !force && isatty(ofd)) {
1.3 mickey 485: if (verbose >= 0)
486: warnx("%s: won't write compressed data to terminal",
1.25 deraadt 487: out);
1.32 mickey 488: (void) close(ofd);
489: (void) close(ifd);
1.37 millert 490: return (FAILURE);
1.3 mickey 491: }
1.1 mickey 492:
1.38 millert 493: if (!pipin && !nosave) {
1.37 millert 494: name = basename(in);
495: mtime = (u_int32_t)sb->st_mtime;
496: }
497: if ((cookie = (*method->open)(ofd, "w", name, bits, mtime, flags)) == NULL) {
1.32 mickey 498: if (verbose >= 0)
1.66 millert 499: warn("%s", out);
500: if (oreg)
501: (void) unlink(out);
1.32 mickey 502: (void) close(ofd);
503: (void) close(ifd);
1.37 millert 504: return (FAILURE);
1.32 mickey 505: }
1.1 mickey 506:
1.32 mickey 507: while ((nr = read(ifd, buf, sizeof(buf))) > 0)
508: if ((method->write)(cookie, buf, nr) != nr) {
509: if (verbose >= 0)
510: warn("%s", out);
1.37 millert 511: error = FAILURE;
1.32 mickey 512: break;
513: }
1.1 mickey 514:
1.32 mickey 515: if (!error && nr < 0) {
516: if (verbose >= 0)
1.1 mickey 517: warn("%s", in);
1.37 millert 518: error = FAILURE;
1.1 mickey 519: }
520:
1.63 otto 521: if ((method->close)(cookie, &info, out, sb)) {
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.66 millert 536: (void) unlink(out);
537: error = WARNING;
1.37 millert 538: }
539:
1.66 millert 540: if (error) {
541: if (oreg)
542: (void) unlink(out);
543: } else if (verbose > 0)
1.37 millert 544: verbose_info(out, info.total_out, info.total_in, info.hlen);
1.44 deraadt 545:
1.21 millert 546: return (error);
1.59 moritz 547: #else
548: warnx("compression not supported");
549: return (FAILURE);
550: #endif
1.1 mickey 551: }
552:
1.18 mickey 553: const struct compressor *
1.37 millert 554: check_method(int fd)
1.1 mickey 555: {
1.18 mickey 556: const struct compressor *method;
1.33 millert 557: u_char magic[2];
1.1 mickey 558:
1.33 millert 559: if (read(fd, magic, sizeof(magic)) != 2)
560: return (NULL);
561: for (method = &c_table[0]; method->name != NULL; method++) {
562: if (magic[0] == method->magic[0] &&
563: magic[1] == method->magic[1])
564: return (method);
565: }
1.45 tedu 566: #ifndef SMALL
567: if (force && cat) {
568: null_magic[0] = magic[0];
569: null_magic[1] = magic[1];
570: return (&null_method);
571: }
572: #endif /* SMALL */
1.33 millert 573: return (NULL);
1.1 mickey 574: }
575:
576: int
1.49 henning 577: dodecompress(const char *in, char *out, const struct compressor *method,
1.24 deraadt 578: int bits, struct stat *sb)
1.1 mickey 579: {
1.18 mickey 580: u_char buf[Z_BUFSIZE];
1.66 millert 581: char oldname[MAXPATHLEN];
582: int error, oreg, ifd, ofd;
1.16 mpech 583: void *cookie;
584: ssize_t nr;
1.37 millert 585: struct z_info info;
1.66 millert 586: struct stat osb;
1.1 mickey 587:
1.66 millert 588: oreg = 0;
1.37 millert 589: error = SUCCESS;
1.1 mickey 590: cookie = NULL;
591:
1.66 millert 592: if (pipin)
593: ifd = dup(STDIN_FILENO);
594: else
595: ifd = open(in, O_RDONLY);
596: if (ifd < 0) {
1.3 mickey 597: if (verbose >= 0)
598: warn("%s", in);
599: return -1;
600: }
601:
602: if (!force && isatty(ifd)) {
603: if (verbose >= 0)
604: warnx("%s: won't read compressed data from terminal",
1.25 deraadt 605: in);
1.3 mickey 606: close (ifd);
607: return -1;
608: }
609:
1.37 millert 610: if ((method = check_method(ifd)) == NULL) {
1.3 mickey 611: if (verbose >= 0)
612: warnx("%s: unrecognized file format", in);
1.13 d 613: close (ifd);
1.3 mickey 614: return -1;
615: }
616:
1.37 millert 617: /* XXX - open constrains outfile to MAXPATHLEN so this is safe */
1.66 millert 618: oldname[0] = '\0';
619: if ((cookie = (*method->open)(ifd, "r", oldname, bits, 0, 1)) == NULL) {
1.32 mickey 620: if (verbose >= 0)
621: warn("%s", in);
622: close (ifd);
1.37 millert 623: return (FAILURE);
1.32 mickey 624: }
1.66 millert 625: if (!nosave && oldname[0] != '\0') {
626: strlcpy(out, oldname, MAXPATHLEN);
627: cat = 0; /* XXX should -c override? */
628: }
1.32 mickey 629:
1.37 millert 630: if (testmode)
631: ofd = -1;
1.66 millert 632: else {
633: if (cat)
634: ofd = dup(STDOUT_FILENO);
635: else {
636: if (stat(out, &osb) == 0) {
637: oreg = S_ISREG(osb.st_mode);
638: if (!force && oreg && !permission(out)) {
639: (void) close(ifd);
640: return (WARNING);
641: }
642: }
643: ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR);
644: }
645: if (ofd < 0) {
646: if (verbose >= 0)
647: warn("%s", in);
648: (method->close)(cookie, NULL, NULL, NULL);
649: return (FAILURE);
650: }
1.32 mickey 651: }
652:
1.37 millert 653: while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) {
654: if (ofd != -1 && write(ofd, buf, nr) != nr) {
1.21 millert 655: if (verbose >= 0)
1.32 mickey 656: warn("%s", out);
1.37 millert 657: error = FAILURE;
1.32 mickey 658: break;
1.21 millert 659: }
1.37 millert 660: }
1.1 mickey 661:
1.32 mickey 662: if (!error && nr < 0) {
663: if (verbose >= 0)
664: warnx("%s: %s", in,
665: errno == EINVAL ? "crc error" : strerror(errno));
1.37 millert 666: error = errno == EINVAL ? WARNING : FAILURE;
1.1 mickey 667: }
668:
1.63 otto 669: if ((method->close)(cookie, &info, NULL, NULL)) {
1.1 mickey 670: if (!error && verbose >= 0)
1.32 mickey 671: warnx("%s", in);
1.37 millert 672: error = FAILURE;
673: }
1.66 millert 674: if (!nosave && !cat) {
1.40 millert 675: if (info.mtime != 0) {
676: sb->st_mtimespec.tv_sec =
677: sb->st_atimespec.tv_sec = info.mtime;
678: sb->st_mtimespec.tv_nsec =
679: sb->st_atimespec.tv_nsec = 0;
680: } else
681: nosave = 1; /* no timestamp to restore */
1.1 mickey 682: }
1.63 otto 683: if (error == SUCCESS)
684: setfile(out, ofd, sb);
1.1 mickey 685:
1.37 millert 686: if (ofd != -1 && close(ofd)) {
1.1 mickey 687: if (!error && verbose >= 0)
1.18 mickey 688: warn("%s", out);
1.37 millert 689: error = FAILURE;
690: }
691:
692: if (!error) {
693: if (list) {
694: if (info.mtime == 0)
695: info.mtime = (u_int32_t)sb->st_mtime;
1.43 millert 696: list_stats(out, method, &info);
1.37 millert 697: } else if (verbose > 0) {
698: verbose_info(out, info.total_in, info.total_out,
699: info.hlen);
700: }
1.1 mickey 701: }
702:
1.66 millert 703: /* On error, clean up the file we created but preserve errno. */
704: if (error && oreg)
705: unlink(out);
706:
1.21 millert 707: return (error);
1.1 mickey 708: }
709:
710: void
1.60 deraadt 711: setfile(const char *name, int fd, struct stat *fs)
1.1 mickey 712: {
1.18 mickey 713: struct timeval tv[2];
1.1 mickey 714:
1.63 otto 715: if (name == NULL || cat || testmode)
1.60 deraadt 716: return;
717:
1.40 millert 718: /*
719: * If input was a pipe we don't have any info to restore but we
720: * must set the mode since the current mode on the file is 0200.
721: */
722: if (pipin) {
723: mode_t mask = umask(022);
1.60 deraadt 724: fchmod(fd, DEFFILEMODE & ~mask);
1.40 millert 725: umask(mask);
726: return;
727: }
1.1 mickey 728:
729: /*
730: * Changing the ownership probably won't succeed, unless we're root
731: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
732: * the mode; current BSD behavior is to remove all setuid bits on
733: * chown. If chown fails, lose setuid/setgid bits.
734: */
1.40 millert 735: fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
1.60 deraadt 736: if (fchown(fd, fs->st_uid, fs->st_gid)) {
1.1 mickey 737: if (errno != EPERM)
1.61 hshoexer 738: warn("fchown: %s", name);
1.1 mickey 739: fs->st_mode &= ~(S_ISUID|S_ISGID);
740: }
1.60 deraadt 741: if (fchmod(fd, fs->st_mode))
1.61 hshoexer 742: warn("fchmod: %s", name);
1.1 mickey 743:
1.60 deraadt 744: if (fs->st_flags && fchflags(fd, fs->st_flags))
1.61 hshoexer 745: warn("fchflags: %s", name);
1.64 millert 746:
1.69 ! millert 747: TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
! 748: TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
! 749: if (futimes(fd, tv))
! 750: warn("futimes: %s", name);
1.1 mickey 751: }
752:
753: int
1.24 deraadt 754: permission(const char *fname)
1.1 mickey 755: {
756: int ch, first;
757:
758: if (!isatty(fileno(stderr)))
759: return (0);
760: (void)fprintf(stderr, "overwrite %s? ", fname);
761: first = ch = getchar();
762: while (ch != '\n' && ch != EOF)
763: ch = getchar();
764: return (first == 'y');
1.35 millert 765: }
766:
767: /*
1.36 millert 768: * Check infile for a known suffix and return the suffix portion or NULL.
769: */
770: const char *
771: check_suffix(const char *infile)
772: {
773: int i;
774: char *suf, *sep, *separators = ".-_";
775: static char *suffixes[] = { "Z", "gz", "z", "tgz", "taz", NULL };
776:
777: for (sep = separators; *sep != '\0'; sep++) {
778: if ((suf = strrchr(infile, *sep)) == NULL)
779: continue;
780: suf++;
781:
782: for (i = 0; suffixes[i] != NULL; i++) {
783: if (strcmp(suf, suffixes[i]) == 0)
784: return (suf - 1);
785: }
786: }
787: return (NULL);
788: }
789:
790: /*
1.35 millert 791: * Set outfile based on the suffix. In most cases we just strip
792: * off the suffix but things like .tgz and .taz are special.
793: */
794: char *
1.36 millert 795: set_outfile(const char *infile, char *outfile, size_t osize)
1.35 millert 796: {
1.36 millert 797: const char *s;
798: char *cp;
799:
800: if ((s = check_suffix(infile)) == NULL)
1.35 millert 801: return (NULL);
802:
1.36 millert 803: (void)strlcpy(outfile, infile, osize);
804: cp = outfile + (s - infile) + 1;
805: /*
806: * Convert tgz and taz -> tar, else drop the suffix.
807: */
808: if (strcmp(cp, "tgz") == 0) {
809: cp[1] = 'a';
810: cp[2] = 'r';
811: } else if (strcmp(cp, "taz") == 0)
812: cp[2] = 'r';
813: else
814: cp[-1] = '\0';
815: return (outfile);
1.37 millert 816: }
817:
818: /*
819: * Print output for the -l option.
820: */
821: void
822: list_stats(const char *name, const struct compressor *method,
823: struct z_info *info)
824: {
825: static off_t compressed_total, uncompressed_total, header_total;
826: static u_int nruns;
827: char *timestr;
1.43 millert 828:
1.37 millert 829: if (nruns == 0) {
830: if (verbose >= 0) {
831: if (verbose > 0)
1.48 millert 832: fputs("method crc date time ", stdout);
833: puts("compressed uncompressed ratio uncompressed_name");
1.37 millert 834: }
835: }
836: nruns++;
837:
838: if (name != NULL) {
839: if (verbose > 0) {
840: timestr = ctime(&info->mtime) + 4;
841: timestr[12] = '\0';
1.48 millert 842: if (timestr[4] == ' ')
843: timestr[4] = '0';
844: printf("%-7.7s %08x %s ", method->name, info->crc,
1.62 deraadt 845: timestr);
1.37 millert 846: }
1.48 millert 847: printf("%10lld %10lld %4.1f%% %s\n",
1.37 millert 848: (long long)(info->total_in + info->hlen),
849: (long long)info->total_out,
1.57 otto 850: ((long long)info->total_out - (long long)info->total_in) *
1.37 millert 851: 100.0 / info->total_out, name);
852: compressed_total += info->total_in;
853: uncompressed_total += info->total_out;
854: header_total += info->hlen;
855: } else if (verbose >= 0) {
856: if (nruns < 3) /* only do totals for > 1 files */
857: return;
858: if (verbose > 0)
1.48 millert 859: fputs(" ", stdout);
860: printf("%10lld %10lld %4.1f%% (totals)\n",
1.37 millert 861: (long long)(compressed_total + header_total),
862: (long long)uncompressed_total,
863: (uncompressed_total - compressed_total) *
864: 100.0 / uncompressed_total);
865: }
866: }
867:
868: void
869: verbose_info(const char *file, off_t compressed, off_t uncompressed,
870: u_int32_t hlen)
871: {
872: if (testmode) {
873: fputs("OK\n", stderr);
874: return;
875: }
876: if (!pipin) {
877: fprintf(stderr, "\t%4.1f%% -- replaced with %s\n",
878: (uncompressed - compressed) * 100.0 / uncompressed, file);
879: }
880: compressed += hlen;
1.44 deraadt 881: fprintf(stderr, "%lld bytes in, %lld bytes out\n",
1.37 millert 882: (long long)(decomp ? compressed : uncompressed),
883: (long long)(decomp ? uncompressed : compressed));
1.1 mickey 884: }
885:
1.31 millert 886: __dead void
887: usage(int status)
1.1 mickey 888: {
889: fprintf(stderr,
1.65 jmc 890: "usage: %s [-123456789cdfghLlNnOqrtVv] [-b bits] [-o filename]\n"
891: "\t[-S suffix] [file ...]\n",
1.18 mickey 892: __progname);
1.31 millert 893: exit(status);
1.1 mickey 894: }