Annotation of src/usr.bin/compress/compress.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: compress.c,v 1.9 1995/03/26 09:44:38 glass Exp $ */
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: #ifndef lint
37: static char copyright[] =
38: "@(#) Copyright (c) 1992, 1993\n\
39: The Regents of the University of California. All rights reserved.\n";
40: #endif /* not lint */
41:
42: #ifndef lint
43: #if 0
44: static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
45: #else
46: static char rcsid[] = "$NetBSD: compress.c,v 1.9 1995/03/26 09:44:38 glass Exp $";
47: #endif
48: #endif /* not lint */
49:
50: #include <sys/param.h>
51: #include <sys/time.h>
52: #include <sys/stat.h>
53:
54: #include <err.h>
55: #include <errno.h>
56: #include <stdio.h>
57: #include <stdlib.h>
58: #include <string.h>
59: #include <unistd.h>
60:
61: #ifdef __STDC__
62: #include <stdarg.h>
63: #else
64: #include <varargs.h>
65: #endif
66:
67: void compress __P((char *, char *, int));
68: void cwarn __P((const char *, ...));
69: void cwarnx __P((const char *, ...));
70: void decompress __P((char *, char *, int));
71: int permission __P((char *));
72: void setfile __P((char *, struct stat *));
73: void usage __P((int));
74:
75: extern FILE *zopen __P((const char *fname, const char *mode, int bits));
76:
77: int eval, force, verbose;
78:
79: int
80: main(argc, argv)
81: int argc;
82: char *argv[];
83: {
84: enum {COMPRESS, DECOMPRESS} style;
85: size_t len;
86: int bits, cat, ch;
87: char *p, newname[MAXPATHLEN];
88:
89: if ((p = rindex(argv[0], '/')) == NULL)
90: p = argv[0];
91: else
92: ++p;
93: if (!strcmp(p, "uncompress"))
94: style = DECOMPRESS;
95: else if (!strcmp(p, "compress"))
96: style = COMPRESS;
97: else
98: errx(1, "unknown program name");
99:
100: bits = cat = 0;
101: while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
102: switch(ch) {
103: case 'b':
104: bits = strtol(optarg, &p, 10);
105: if (*p)
106: errx(1, "illegal bit count -- %s", optarg);
107: break;
108: case 'c':
109: cat = 1;
110: break;
111: case 'd': /* Backward compatible. */
112: style = DECOMPRESS;
113: break;
114: case 'f':
115: force = 1;
116: break;
117: case 'v':
118: verbose = 1;
119: break;
120: case '?':
121: default:
122: usage(style == COMPRESS);
123: }
124: argc -= optind;
125: argv += optind;
126:
127: if (argc == 0) {
128: switch(style) {
129: case COMPRESS:
130: (void)compress("/dev/stdin", "/dev/stdout", bits);
131: break;
132: case DECOMPRESS:
133: (void)decompress("/dev/stdin", "/dev/stdout", bits);
134: break;
135: }
136: exit (eval);
137: }
138:
139: if (cat == 1 && argc > 1)
140: errx(1, "the -c option permits only a single file argument");
141:
142: for (; *argv; ++argv)
143: switch(style) {
144: case COMPRESS:
145: if (cat) {
146: compress(*argv, "/dev/stdout", bits);
147: break;
148: }
149: if ((p = rindex(*argv, '.')) != NULL &&
150: !strcmp(p, ".Z")) {
151: cwarnx("%s: name already has trailing .Z",
152: *argv);
153: break;
154: }
155: len = strlen(*argv);
156: if (len > sizeof(newname) - 3) {
157: cwarnx("%s: name too long", *argv);
158: break;
159: }
160: memmove(newname, *argv, len);
161: newname[len] = '.';
162: newname[len + 1] = 'Z';
163: newname[len + 2] = '\0';
164: compress(*argv, newname, bits);
165: break;
166: case DECOMPRESS:
167: len = strlen(*argv);
168: if ((p = rindex(*argv, '.')) == NULL ||
169: strcmp(p, ".Z")) {
170: if (len > sizeof(newname) - 3) {
171: cwarnx("%s: name too long", *argv);
172: break;
173: }
174: memmove(newname, *argv, len);
175: newname[len] = '.';
176: newname[len + 1] = 'Z';
177: newname[len + 2] = '\0';
178: decompress(newname,
179: cat ? "/dev/stdout" : *argv, bits);
180: } else {
181: if (len - 2 > sizeof(newname) - 1) {
182: cwarnx("%s: name too long", *argv);
183: break;
184: }
185: memmove(newname, *argv, len - 2);
186: newname[len - 2] = '\0';
187: decompress(*argv,
188: cat ? "/dev/stdout" : newname, bits);
189: }
190: break;
191: }
192: exit (eval);
193: }
194:
195: void
196: compress(in, out, bits)
197: char *in, *out;
198: int bits;
199: {
200: register int nr;
201: struct stat isb, sb;
202: FILE *ifp, *ofp;
203: int exists, isreg, oreg;
204: u_char buf[1024];
205:
206: exists = !stat(out, &sb);
207: if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
208: return;
209: isreg = oreg = !exists || S_ISREG(sb.st_mode);
210:
211: ifp = ofp = NULL;
212: if ((ifp = fopen(in, "r")) == NULL) {
213: cwarn("%s", in);
214: return;
215: }
216: if (stat(in, &isb)) { /* DON'T FSTAT! */
217: cwarn("%s", in);
218: goto err;
219: }
220: if (!S_ISREG(isb.st_mode))
221: isreg = 0;
222:
223: if ((ofp = zopen(out, "w", bits)) == NULL) {
224: cwarn("%s", out);
225: goto err;
226: }
227: while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
228: if (fwrite(buf, 1, nr, ofp) != nr) {
229: cwarn("%s", out);
230: goto err;
231: }
232:
233: if (ferror(ifp) || fclose(ifp)) {
234: cwarn("%s", in);
235: goto err;
236: }
237: ifp = NULL;
238:
239: if (fclose(ofp)) {
240: cwarn("%s", out);
241: goto err;
242: }
243: ofp = NULL;
244:
245: if (isreg) {
246: if (stat(out, &sb)) {
247: cwarn("%s", out);
248: goto err;
249: }
250:
251: if (!force && sb.st_size >= isb.st_size) {
252: if (verbose)
253: (void)printf("%s: file would grow; left unmodified\n", in);
254: if (unlink(out))
255: cwarn("%s", out);
256: goto err;
257: }
258:
259: setfile(out, &isb);
260:
261: if (unlink(in))
262: cwarn("%s", in);
263:
264: if (verbose) {
265: (void)printf("%s: ", out);
266: if (isb.st_size > sb.st_size)
267: (void)printf("%.0f%% compression\n",
268: ((float)sb.st_size / isb.st_size) * 100.0);
269: else
270: (void)printf("%.0f%% expansion\n",
271: ((float)isb.st_size / sb.st_size) * 100.0);
272: }
273: }
274: return;
275:
276: err: if (ofp) {
277: if (oreg)
278: (void)unlink(out);
279: (void)fclose(ofp);
280: }
281: if (ifp)
282: (void)fclose(ifp);
283: }
284:
285: void
286: decompress(in, out, bits)
287: char *in, *out;
288: int bits;
289: {
290: register int nr;
291: struct stat sb;
292: FILE *ifp, *ofp;
293: int exists, isreg, oreg;
294: u_char buf[1024];
295:
296: exists = !stat(out, &sb);
297: if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
298: return;
299: isreg = oreg = !exists || S_ISREG(sb.st_mode);
300:
301: ifp = ofp = NULL;
302: if ((ofp = fopen(out, "w")) == NULL) {
303: cwarn("%s", out);
304: return;
305: }
306:
307: if ((ifp = zopen(in, "r", bits)) == NULL) {
308: cwarn("%s", in);
309: goto err;
310: }
311: if (stat(in, &sb)) {
312: cwarn("%s", in);
313: goto err;
314: }
315: if (!S_ISREG(sb.st_mode))
316: isreg = 0;
317:
318: while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
319: if (fwrite(buf, 1, nr, ofp) != nr) {
320: cwarn("%s", out);
321: goto err;
322: }
323:
324: if (ferror(ifp) || fclose(ifp)) {
325: cwarn("%s", in);
326: goto err;
327: }
328: ifp = NULL;
329:
330: if (fclose(ofp)) {
331: cwarn("%s", out);
332: goto err;
333: }
334:
335: if (isreg) {
336: setfile(out, &sb);
337:
338: if (unlink(in))
339: cwarn("%s", in);
340: }
341: return;
342:
343: err: if (ofp) {
344: if (oreg)
345: (void)unlink(out);
346: (void)fclose(ofp);
347: }
348: if (ifp)
349: (void)fclose(ifp);
350: }
351:
352: void
353: setfile(name, fs)
354: char *name;
355: register struct stat *fs;
356: {
357: static struct timeval tv[2];
358:
359: fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
360:
361: TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
362: TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
363: if (utimes(name, tv))
364: cwarn("utimes: %s", name);
365:
366: /*
367: * Changing the ownership probably won't succeed, unless we're root
368: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
369: * the mode; current BSD behavior is to remove all setuid bits on
370: * chown. If chown fails, lose setuid/setgid bits.
371: */
372: if (chown(name, fs->st_uid, fs->st_gid)) {
373: if (errno != EPERM)
374: cwarn("chown: %s", name);
375: fs->st_mode &= ~(S_ISUID|S_ISGID);
376: }
377: if (chmod(name, fs->st_mode))
378: cwarn("chown: %s", name);
379:
380: if (chflags(name, fs->st_flags))
381: cwarn("chflags: %s", name);
382: }
383:
384: int
385: permission(fname)
386: char *fname;
387: {
388: int ch, first;
389:
390: if (!isatty(fileno(stderr)))
391: return (0);
392: (void)fprintf(stderr, "overwrite %s? ", fname);
393: first = ch = getchar();
394: while (ch != '\n' && ch != EOF)
395: ch = getchar();
396: return (first == 'y');
397: }
398:
399: void
400: usage(iscompress)
401: int iscompress;
402: {
403: if (iscompress)
404: (void)fprintf(stderr,
405: "usage: compress [-cfv] [-b bits] [file ...]\n");
406: else
407: (void)fprintf(stderr,
408: "usage: uncompress [-c] [-b bits] [file ...]\n");
409: exit(1);
410: }
411:
412: void
413: #if __STDC__
414: cwarnx(const char *fmt, ...)
415: #else
416: cwarnx(fmt, va_alist)
417: int eval;
418: const char *fmt;
419: va_dcl
420: #endif
421: {
422: va_list ap;
423: #if __STDC__
424: va_start(ap, fmt);
425: #else
426: va_start(ap);
427: #endif
428: vwarnx(fmt, ap);
429: va_end(ap);
430: eval = 1;
431: }
432:
433: void
434: #if __STDC__
435: cwarn(const char *fmt, ...)
436: #else
437: cwarn(fmt, va_alist)
438: int eval;
439: const char *fmt;
440: va_dcl
441: #endif
442: {
443: va_list ap;
444: #if __STDC__
445: va_start(ap, fmt);
446: #else
447: va_start(ap);
448: #endif
449: vwarn(fmt, ap);
450: va_end(ap);
451: eval = 1;
452: }