Annotation of src/usr.bin/compress/main.c, Revision 1.14
1.14 ! millert 1: /* $OpenBSD: main.c,v 1.13 2000/03/10 06:53:51 d Exp $ */
1.1 mickey 2:
3: /*-
4: * Copyright (c) 1992, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: static char copyright[] =
37: "@(#) Copyright (c) 1992, 1993\n\
38: The Regents of the University of California. All rights reserved.\n";
39:
40: #ifndef lint
41: #if 0
42: static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
43: #else
1.14 ! millert 44: static char rcsid[] = "$OpenBSD: main.c,v 1.13 2000/03/10 06:53:51 d Exp $";
1.1 mickey 45: #endif
46: #endif /* not lint */
47:
48: #include <sys/param.h>
49: #include <sys/time.h>
50: #include <sys/stat.h>
51:
52: #include <err.h>
53: #include <errno.h>
54: #include <stdio.h>
55: #include <stdlib.h>
56: #include <string.h>
57: #include <unistd.h>
58: #include <fcntl.h>
59: #include <paths.h>
60: #include "compress.h"
61:
62: #define min(a,b) ((a) < (b)? (a) : (b))
63:
1.9 mickey 64: int pipin = 0, force = 0, verbose = 0, testmode = 0, list = 0, nosave = 0;
1.1 mickey 65: extern char *__progname;
66:
67: struct compressor {
68: char *name;
69: char *suffix;
70: int (*check_header) __P((int, struct stat *, const char *));
71: void *(*open) __P((int, const char *, int));
72: int (*read) __P((void *, char *, int));
73: int (*write) __P((void *, const char *, int));
74: int (*close) __P((void *));
75: } c_table[] = {
76: #define M_COMPRESS (&c_table[0])
77: { "compress", ".Z", z_check_header, z_open, zread, zwrite, zclose },
78: #define M_DEFLATE (&c_table[1])
79: { "deflate", ".gz", gz_check_header, gz_open, gz_read, gz_write, gz_close },
80: { NULL }
81: };
82:
83: int permission __P((char *));
84: void setfile __P((char *, struct stat *));
85: void usage __P((void));
86: int compress
87: __P((const char *, const char *, register struct compressor *, int));
88: int decompress
89: __P((const char *, const char *, register struct compressor *, int));
90: struct compressor *check_method __P((int, const char *));
91:
92: struct stat sb, osb;
93:
94: int
95: main(argc, argv)
96: int argc;
97: char *argv[];
98: {
99: int ch, bits, cat, decomp, error;
100: struct compressor *method;
101: int exists, isreg, oreg;
1.7 denny 102: char *infile, outfile[MAXPATHLEN+4], suffix[16];
1.1 mickey 103: char *p;
1.7 denny 104: int rc = 0;
1.1 mickey 105:
1.6 mickey 106: bits = cat = decomp = 0;
1.1 mickey 107: p = __progname;
108: if (p[0] == 'g') {
109: method = M_DEFLATE;
110: p++;
111: } else
112: method = M_COMPRESS;
113:
114: decomp = 0;
115: if (!strcmp(p, "zcat")) {
116: decomp++;
117: cat++;
118: } else {
119: if (p[0] == 'u' && p[1] == 'n') {
120: p += 2;
121: decomp++;
122: }
123:
1.2 mickey 124: if (strcmp(p, "zip") &&
125: strcmp(p, "compress"))
1.1 mickey 126: errx(1, "unknown program name");
127: }
128:
129: outfile[0] = '\0';
130: while ((ch = getopt(argc, argv, "0123456789b:cdfghlnOo:qStv")) != -1)
131: switch(ch) {
132: case '0':
133: case '1':
134: case '2':
135: case '3':
136: case '4':
137: case '5':
138: case '6':
139: case '7':
140: case '8':
141: case '9':
142: method = M_DEFLATE;
143: bits = ch - '0';
144: break;
145: case 'b':
146: bits = strtol(optarg, &p, 10);
1.7 denny 147: /*
148: * POSIX 1002.3 says 9 <= bits <= 14 for portable
149: * apps, but says the implementation may allow
150: * greater.
151: */
1.1 mickey 152: if (*p)
153: errx(1, "illegal bit count -- %s", optarg);
154: break;
155: case 'c':
156: cat++;
157: break;
158: case 'd': /* Backward compatible. */
159: decomp++;
160: break;
161: case 'f':
162: force++;
163: break;
164: case 'g':
165: method = M_DEFLATE;
166: break;
167: case 'l':
168: list++;
169: break;
170: case 'L':
171: fputs(copyright, stderr);
172: case 'n':
173: nosave++;
174: break;
175: case 'N':
176: nosave = 0;
177: break;
178: case 'O':
179: method = M_COMPRESS;
180: break;
181: case 'o':
182: strncpy(outfile, optarg, sizeof(outfile)-1);
1.10 deraadt 183: outfile[sizeof(outfile)-1] = '\0';
1.1 mickey 184: break;
185: case 'q':
186: verbose = -1;
187: break;
188: case 'S':
189: p = suffix;
190: if (optarg[0] != '.')
191: *p++ = '.';
192: strncpy(p, optarg, sizeof(suffix) - (p - suffix) - 1);
193: break;
194: case 't':
195: testmode++;
196: break;
197: case 'v':
198: verbose++;
199: break;
200: case 'h':
201: case '?':
202: default:
203: usage();
204: }
205: argc -= optind;
206: argv += optind;
207:
208: do {
209: if (*argv != NULL) {
210: infile = *argv;
211: if (outfile[0] == '\0') {
1.7 denny 212: if (!decomp && !cat && outfile[0] == '\0') {
213: int len;
214: char *p;
215:
216: snprintf(outfile, sizeof(outfile),
217: "%s%s", infile,
218: method->suffix);
219:
220: len = strlen(outfile);
221: if (len > MAXPATHLEN) {
222: errx(1, "pathname%s too long",
223: method->suffix);
224: }
225:
226: p = strrchr(outfile, '/');
227: if (p == NULL) p = outfile;
228: len = strlen(p);
229: if (len > NAME_MAX) {
230: errx(1, "filename%s too long",
231: method->suffix);
232: }
233: } else if (decomp && !cat) {
1.1 mickey 234: char *p = strrchr(infile, '.');
235: if (p != NULL)
236: for (method = &c_table[0];
237: method->name != NULL &&
238: !strcmp(p, method->suffix);
239: method++)
240: ;
241: if (method->name != NULL) {
242: int l = min(sizeof(outfile),
243: (p - infile));
244: strncpy(outfile, infile, l);
245: outfile[l] = '\0';
246: }
247: }
248: }
1.9 mickey 249: } else {
1.1 mickey 250: infile = "/dev/stdin";
1.9 mickey 251: pipin++;
252: }
1.1 mickey 253:
254: if (testmode)
255: strcpy(outfile, _PATH_DEVNULL);
256: else if (cat || outfile[0] == '\0') {
257: strcpy(outfile, "/dev/stdout");
258: cat++;
259: }
260:
261: exists = !stat(outfile, &sb);
262: if (!force && exists && S_ISREG(sb.st_mode) &&
263: !permission(outfile)) {
264: argv++;
265: continue;
266: }
267: isreg = oreg = !exists || S_ISREG(sb.st_mode);
268:
269: if (stat(infile, &sb) != 0 && verbose >= 0)
1.14 ! millert 270: err(1, "%s", infile);
1.1 mickey 271:
272: if (!S_ISREG(sb.st_mode))
273: isreg = 0;
274:
275: if (verbose > 0)
276: fprintf(stderr, "%s:\t", infile);
277:
278: error = (decomp? decompress: compress)
279: (infile, outfile, method, bits);
280:
281: if (!error && isreg && stat(outfile, &osb) == 0) {
282:
283: if (!force && !decomp && osb.st_size >= sb.st_size) {
284: if (verbose > 0)
285: fprintf(stderr, "file would grow; "
286: "left unmodified\n");
287: error = 1;
1.7 denny 288: rc = 2;
1.1 mickey 289: } else {
290:
291: setfile(outfile, &sb);
292:
293: if (unlink(infile) && verbose >= 0)
294: warn("%s", infile);
295:
296: if (verbose > 0) {
297: u_int ratio;
298: ratio = (1000*osb.st_size)/sb.st_size;
299: fprintf(stderr, "%u", ratio / 10);
300: if (ratio % 10)
301: fprintf(stderr, ".%u",
302: ratio % 10);
303: fputc('%', stderr);
304: fputc(' ', stderr);
305: }
306: }
307: }
308:
1.5 mickey 309: if (error && oreg && unlink(outfile) && errno != ENOENT &&
310: verbose >= 0)
1.1 mickey 311: warn("%s", outfile);
312: else if (!error && verbose > 0)
313: fputs("OK\n", stderr);
314:
315: outfile[0] = '\0';
316: if (*argv != NULL)
317: argv++;
318:
319: } while (*argv != NULL);
320:
1.7 denny 321: return (rc);
1.1 mickey 322: }
323:
324: int
325: compress(in, out, method, bits)
326: const char *in;
327: const char *out;
328: register struct compressor *method;
329: int bits;
330: {
331: register int ifd;
332: int ofd;
333: register void *cookie;
1.11 espie 334: register ssize_t nr;
1.1 mickey 335: u_char buf[Z_BUFSIZE];
336: int error;
337:
338: error = 0;
339: cookie = NULL;
1.3 mickey 340:
341: if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) {
342: if (verbose >= 0)
343: warn("%s", out);
344: return -1;
345: }
346:
1.4 mickey 347: if (method != M_COMPRESS && !force && isatty(ofd)) {
1.3 mickey 348: if (verbose >= 0)
349: warnx("%s: won't write compressed data to terminal",
350: out);
351: return -1;
352: }
1.1 mickey 353:
354: if ((ifd = open(in, O_RDONLY)) >= 0 &&
355: (cookie = (*method->open)(ofd, "w", bits)) != NULL) {
356:
1.12 mickey 357: while ((nr = read(ifd, buf, sizeof(buf))) > 0)
1.1 mickey 358: if ((method->write)(cookie, buf, nr) != nr) {
359: if (verbose >= 0)
360: warn("%s", out);
361: error++;
362: break;
363: }
364: }
365:
1.12 mickey 366: if (ifd < 0 || close(ifd) || nr < 0) {
1.1 mickey 367: if (!error && verbose >= 0)
368: warn("%s", in);
369: error++;
370: }
371:
1.3 mickey 372: if (cookie == NULL || (method->close)(cookie)) {
1.1 mickey 373: if (!error && verbose >= 0)
374: warn("%s", out);
375: error++;
376: (void) close(ofd);
377: }
378:
379: return error? -1 : 0;
380: }
381:
382: struct compressor *
383: check_method(fd, out)
384: int fd;
385: const char *out;
386: {
387: register struct compressor *method;
388:
389: for (method = &c_table[0];
390: method->name != NULL &&
391: !(*method->check_header)(fd, &sb, out);
392: method++)
393: ;
394:
395: if (method->name == NULL)
396: method = NULL;
397:
398: return method;
399: }
400:
401: int
402: decompress(in, out, method, bits)
403: const char *in;
404: const char *out;
405: register struct compressor *method;
406: int bits;
407: {
408: int ifd;
409: register int ofd;
410: register void *cookie;
1.11 espie 411: register ssize_t nr;
1.1 mickey 412: u_char buf[Z_BUFSIZE];
413: int error;
414:
415: error = 0;
416: cookie = NULL;
417:
1.3 mickey 418: if ((ifd = open(in, O_RDONLY)) < 0) {
419: if (verbose >= 0)
420: warn("%s", in);
421: return -1;
422: }
423:
424: if (!force && isatty(ifd)) {
425: if (verbose >= 0)
426: warnx("%s: won't read compressed data from terminal",
427: in);
428: close (ifd);
429: return -1;
430: }
431:
1.9 mickey 432: if (!pipin && (method = check_method(ifd, out)) == NULL) {
1.3 mickey 433: if (verbose >= 0)
434: warnx("%s: unrecognized file format", in);
1.13 d 435: close (ifd);
1.3 mickey 436: return -1;
437: }
438:
1.1 mickey 439: if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) >= 0 &&
440: (cookie = (*method->open)(ifd, "r", bits)) != NULL) {
441:
1.12 mickey 442: while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0)
1.1 mickey 443: if (write(ofd, buf, nr) != nr) {
444: if (verbose >= 0)
445: warn("%s", out);
446: error++;
447: break;
448: }
449: }
450:
451: if (ofd < 0 || close(ofd)) {
452: if (!error && verbose >= 0)
453: warn("%s", out);
454: error++;
455: }
456:
1.12 mickey 457: if (cookie == NULL || (method->close)(cookie) || nr < 0) {
1.1 mickey 458: if (!error && verbose >= 0)
1.3 mickey 459: warn("%s", in);
1.1 mickey 460: error++;
1.3 mickey 461: (void) close (ifd);
1.1 mickey 462: }
463:
464: return error;
465: }
466:
467: void
468: setfile(name, fs)
469: char *name;
470: register struct stat *fs;
471: {
472: static struct timeval tv[2];
473:
474: fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
475:
476: TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
477: TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
478: if (utimes(name, tv))
479: warn("utimes: %s", name);
480:
481: /*
482: * Changing the ownership probably won't succeed, unless we're root
483: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
484: * the mode; current BSD behavior is to remove all setuid bits on
485: * chown. If chown fails, lose setuid/setgid bits.
486: */
487: if (chown(name, fs->st_uid, fs->st_gid)) {
488: if (errno != EPERM)
489: warn("chown: %s", name);
490: fs->st_mode &= ~(S_ISUID|S_ISGID);
491: }
492: if (chmod(name, fs->st_mode))
493: warn("chown: %s", name);
494:
1.8 millert 495: if (fs->st_flags && chflags(name, fs->st_flags))
1.1 mickey 496: warn("chflags: %s", name);
497: }
498:
499: int
500: permission(fname)
501: char *fname;
502: {
503: int ch, first;
504:
505: if (!isatty(fileno(stderr)))
506: return (0);
507: (void)fprintf(stderr, "overwrite %s? ", fname);
508: first = ch = getchar();
509: while (ch != '\n' && ch != EOF)
510: ch = getchar();
511: return (first == 'y');
512: }
513:
514: void
515: usage()
516: {
517: fprintf(stderr,
518: "usage: %s [-cdfghlnOtqv] [-b <bits>] [-[0-9]] [file ...]\n",
519: __progname);
520: exit(1);
521: }
522: