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