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