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