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