1a9024bf6Sbostic /*-
2faf4f947Sbostic * Copyright (c) 1992, 1993
3faf4f947Sbostic * The Regents of the University of California. All rights reserved.
469df8f59Sbostic *
5761482d9Sbostic * %sccs.include.redist.c%
669df8f59Sbostic */
769df8f59Sbostic
8782a8f7dSmckusick #ifndef lint
9faf4f947Sbostic static char copyright[] =
10faf4f947Sbostic "@(#) Copyright (c) 1992, 1993\n\
11faf4f947Sbostic The Regents of the University of California. All rights reserved.\n";
1269df8f59Sbostic #endif /* not lint */
1369df8f59Sbostic
1469df8f59Sbostic #ifndef lint
15*d3ed61d9Sbostic static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 01/07/94";
1669df8f59Sbostic #endif /* not lint */
17782a8f7dSmckusick
188c01a94eSbostic #include <sys/param.h>
19a9024bf6Sbostic #include <sys/time.h>
208c01a94eSbostic #include <sys/stat.h>
21a9024bf6Sbostic
2273eeb121Sbostic #include <err.h>
238222a9e9Sbostic #include <errno.h>
248c01a94eSbostic #include <stdio.h>
258c01a94eSbostic #include <stdlib.h>
268c01a94eSbostic #include <string.h>
27a9024bf6Sbostic #include <unistd.h>
28c3545c8eSmckusick
2973eeb121Sbostic #ifdef __STDC__
3073eeb121Sbostic #include <stdarg.h>
3173eeb121Sbostic #else
3273eeb121Sbostic #include <varargs.h>
3373eeb121Sbostic #endif
3473eeb121Sbostic
35a9024bf6Sbostic void compress __P((char *, char *, int));
3673eeb121Sbostic void cwarn __P((const char *, ...));
3773eeb121Sbostic void cwarnx __P((const char *, ...));
38a9024bf6Sbostic void decompress __P((char *, char *, int));
39a9024bf6Sbostic int permission __P((char *));
40fcb61de9Sbostic void setfile __P((char *, struct stat *));
41a9024bf6Sbostic void usage __P((int));
42782a8f7dSmckusick
43a9024bf6Sbostic int eval, force, verbose;
446c8aaa2bSlepreau
45a9024bf6Sbostic int
main(argc,argv)46782a8f7dSmckusick main(argc, argv)
478c01a94eSbostic int argc;
48a9024bf6Sbostic char *argv[];
49782a8f7dSmckusick {
50a9024bf6Sbostic enum {COMPRESS, DECOMPRESS} style;
51a9024bf6Sbostic size_t len;
52a9024bf6Sbostic int bits, cat, ch;
53a9024bf6Sbostic char *p, newname[MAXPATHLEN];
54782a8f7dSmckusick
55a9024bf6Sbostic if ((p = rindex(argv[0], '/')) == NULL)
56a9024bf6Sbostic p = argv[0];
578c01a94eSbostic else
58a9024bf6Sbostic ++p;
59afb7e265Sbostic if (!strcmp(p, "uncompress"))
60a9024bf6Sbostic style = DECOMPRESS;
61afb7e265Sbostic else if (!strcmp(p, "compress"))
62a9024bf6Sbostic style = COMPRESS;
63afb7e265Sbostic else
6473eeb121Sbostic errx(1, "unknown program name");
65782a8f7dSmckusick
66a9024bf6Sbostic bits = cat = 0;
67a9024bf6Sbostic while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
688c01a94eSbostic switch(ch) {
698c01a94eSbostic case 'b':
70a9024bf6Sbostic bits = strtol(optarg, &p, 10);
71a9024bf6Sbostic if (*p)
7273eeb121Sbostic errx(1, "illegal bit count -- %s", optarg);
738c01a94eSbostic break;
748c01a94eSbostic case 'c':
75a9024bf6Sbostic cat = 1;
768c01a94eSbostic break;
77a9024bf6Sbostic case 'd': /* Backward compatible. */
78a9024bf6Sbostic style = DECOMPRESS;
79782a8f7dSmckusick break;
80782a8f7dSmckusick case 'f':
81c3545c8eSmckusick force = 1;
82782a8f7dSmckusick break;
838c01a94eSbostic case 'v':
84a9024bf6Sbostic verbose = 1;
858c01a94eSbostic break;
868c01a94eSbostic case '?':
87782a8f7dSmckusick default:
88a9024bf6Sbostic usage(style == COMPRESS);
89782a8f7dSmckusick }
908c01a94eSbostic argc -= optind;
918c01a94eSbostic argv += optind;
92782a8f7dSmckusick
93a9024bf6Sbostic if (argc == 0) {
94a9024bf6Sbostic switch(style) {
95a9024bf6Sbostic case COMPRESS:
96a9024bf6Sbostic (void)compress("/dev/stdin", "/dev/stdout", bits);
97a9024bf6Sbostic break;
98a9024bf6Sbostic case DECOMPRESS:
99a9024bf6Sbostic (void)decompress("/dev/stdin", "/dev/stdout", bits);
100a9024bf6Sbostic break;
101782a8f7dSmckusick }
102a9024bf6Sbostic exit (eval);
103782a8f7dSmckusick }
104782a8f7dSmckusick
105a9024bf6Sbostic if (cat == 1 && argc > 1)
10673eeb121Sbostic errx(1, "the -c option permits only a single file argument");
107a9024bf6Sbostic
108a9024bf6Sbostic for (; *argv; ++argv)
109a9024bf6Sbostic switch(style) {
110a9024bf6Sbostic case COMPRESS:
111a9024bf6Sbostic if (cat) {
112a9024bf6Sbostic compress(*argv, "/dev/stdout", bits);
113a9024bf6Sbostic break;
114782a8f7dSmckusick }
115a9024bf6Sbostic if ((p = rindex(*argv, '.')) != NULL &&
116a9024bf6Sbostic !strcmp(p, ".Z")) {
11773eeb121Sbostic cwarnx("%s: name already has trailing .Z",
118a9024bf6Sbostic *argv);
119a9024bf6Sbostic break;
120782a8f7dSmckusick }
121a9024bf6Sbostic len = strlen(*argv);
122a9024bf6Sbostic if (len > sizeof(newname) - 3) {
12373eeb121Sbostic cwarnx("%s: name too long", *argv);
124a9024bf6Sbostic break;
125a9024bf6Sbostic }
126a9024bf6Sbostic memmove(newname, *argv, len);
127a9024bf6Sbostic newname[len] = '.';
128a9024bf6Sbostic newname[len + 1] = 'Z';
129a9024bf6Sbostic newname[len + 2] = '\0';
130a9024bf6Sbostic compress(*argv, newname, bits);
131a9024bf6Sbostic break;
132a9024bf6Sbostic case DECOMPRESS:
133a9024bf6Sbostic len = strlen(*argv);
134fcb61de9Sbostic if ((p = rindex(*argv, '.')) == NULL ||
135fcb61de9Sbostic strcmp(p, ".Z")) {
136a9024bf6Sbostic if (len > sizeof(newname) - 3) {
13773eeb121Sbostic cwarnx("%s: name too long", *argv);
138a9024bf6Sbostic break;
139a9024bf6Sbostic }
140a9024bf6Sbostic memmove(newname, *argv, len);
141a9024bf6Sbostic newname[len] = '.';
142a9024bf6Sbostic newname[len + 1] = 'Z';
143a9024bf6Sbostic newname[len + 2] = '\0';
144a9024bf6Sbostic decompress(newname,
145a9024bf6Sbostic cat ? "/dev/stdout" : *argv, bits);
146782a8f7dSmckusick } else {
147a9024bf6Sbostic if (len - 2 > sizeof(newname) - 1) {
14873eeb121Sbostic cwarnx("%s: name too long", *argv);
149a9024bf6Sbostic break;
150782a8f7dSmckusick }
151a9024bf6Sbostic memmove(newname, *argv, len - 2);
152a9024bf6Sbostic newname[len - 2] = '\0';
153a9024bf6Sbostic decompress(*argv,
154a9024bf6Sbostic cat ? "/dev/stdout" : newname, bits);
155782a8f7dSmckusick }
156a9024bf6Sbostic break;
157782a8f7dSmckusick }
158a9024bf6Sbostic exit (eval);
159782a8f7dSmckusick }
160782a8f7dSmckusick
161a9024bf6Sbostic void
compress(in,out,bits)162a9024bf6Sbostic compress(in, out, bits)
163a9024bf6Sbostic char *in, *out;
164a9024bf6Sbostic int bits;
1658222a9e9Sbostic {
166a9024bf6Sbostic register int nr;
167fcb61de9Sbostic struct stat isb, sb;
168a9024bf6Sbostic FILE *ifp, *ofp;
169a902403dSbostic int exists, isreg, oreg;
170a9024bf6Sbostic u_char buf[1024];
171782a8f7dSmckusick
172a9024bf6Sbostic exists = !stat(out, &sb);
173a9024bf6Sbostic if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
174a9024bf6Sbostic return;
175a902403dSbostic isreg = oreg = !exists || S_ISREG(sb.st_mode);
176a9024bf6Sbostic
177a9024bf6Sbostic ifp = ofp = NULL;
178a9024bf6Sbostic if ((ifp = fopen(in, "r")) == NULL) {
17973eeb121Sbostic cwarn("%s", in);
180a9024bf6Sbostic return;
181782a8f7dSmckusick }
182fcb61de9Sbostic if (stat(in, &isb)) { /* DON'T FSTAT! */
18373eeb121Sbostic cwarn("%s", in);
184a9024bf6Sbostic goto err;
185782a8f7dSmckusick }
186fcb61de9Sbostic if (!S_ISREG(isb.st_mode))
187a902403dSbostic isreg = 0;
188782a8f7dSmckusick
189a9024bf6Sbostic if ((ofp = zopen(out, "w", bits)) == NULL) {
19073eeb121Sbostic cwarn("%s", out);
191a9024bf6Sbostic goto err;
192782a8f7dSmckusick }
193a9024bf6Sbostic while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
194a9024bf6Sbostic if (fwrite(buf, 1, nr, ofp) != nr) {
19573eeb121Sbostic cwarn("%s", out);
196a9024bf6Sbostic goto err;
197a9024bf6Sbostic }
198a9024bf6Sbostic
199a9024bf6Sbostic if (ferror(ifp) || fclose(ifp)) {
20073eeb121Sbostic cwarn("%s", in);
201a9024bf6Sbostic goto err;
202a9024bf6Sbostic }
203a9024bf6Sbostic ifp = NULL;
204a9024bf6Sbostic
205a9024bf6Sbostic if (fclose(ofp)) {
20673eeb121Sbostic cwarn("%s", out);
207a9024bf6Sbostic goto err;
208a9024bf6Sbostic }
209a9024bf6Sbostic ofp = NULL;
210a9024bf6Sbostic
211a902403dSbostic if (isreg) {
212a9024bf6Sbostic if (stat(out, &sb)) {
21373eeb121Sbostic cwarn("%s", out);
214a9024bf6Sbostic goto err;
215a9024bf6Sbostic }
216a902403dSbostic
217fcb61de9Sbostic if (!force && sb.st_size >= isb.st_size) {
218a9024bf6Sbostic if (verbose)
219a9024bf6Sbostic (void)printf("%s: file would grow; left unmodified\n", in);
220a9024bf6Sbostic if (unlink(out))
22173eeb121Sbostic cwarn("%s", out);
222a9024bf6Sbostic goto err;
223a9024bf6Sbostic }
224a9024bf6Sbostic
225fcb61de9Sbostic setfile(out, &isb);
226a9024bf6Sbostic
227fcb61de9Sbostic if (unlink(in))
22873eeb121Sbostic cwarn("%s", in);
229a9024bf6Sbostic
230a902403dSbostic if (verbose) {
231a9024bf6Sbostic (void)printf("%s: ", out);
232fcb61de9Sbostic if (isb.st_size > sb.st_size)
233a9024bf6Sbostic (void)printf("%.0f%% compression\n",
234fcb61de9Sbostic ((float)sb.st_size / isb.st_size) * 100.0);
235a9024bf6Sbostic else
236a9024bf6Sbostic (void)printf("%.0f%% expansion\n",
237fcb61de9Sbostic ((float)isb.st_size / sb.st_size) * 100.0);
238a9024bf6Sbostic }
239a902403dSbostic }
240a9024bf6Sbostic return;
241a9024bf6Sbostic
242a9024bf6Sbostic err: if (ofp) {
243a9024bf6Sbostic if (oreg)
244a9024bf6Sbostic (void)unlink(out);
245a9024bf6Sbostic (void)fclose(ofp);
246a9024bf6Sbostic }
247a9024bf6Sbostic if (ifp)
248a9024bf6Sbostic (void)fclose(ifp);
249a9024bf6Sbostic }
250a9024bf6Sbostic
251a9024bf6Sbostic void
decompress(in,out,bits)252a9024bf6Sbostic decompress(in, out, bits)
253a9024bf6Sbostic char *in, *out;
254a9024bf6Sbostic int bits;
255a9024bf6Sbostic {
256a9024bf6Sbostic register int nr;
257a9024bf6Sbostic struct stat sb;
258a9024bf6Sbostic FILE *ifp, *ofp;
259fcb61de9Sbostic int exists, isreg, oreg;
260a9024bf6Sbostic u_char buf[1024];
261a9024bf6Sbostic
262fcb61de9Sbostic exists = !stat(out, &sb);
263fcb61de9Sbostic if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
264a9024bf6Sbostic return;
265fcb61de9Sbostic isreg = oreg = !exists || S_ISREG(sb.st_mode);
266a9024bf6Sbostic
267a9024bf6Sbostic ifp = ofp = NULL;
268a9024bf6Sbostic if ((ofp = fopen(out, "w")) == NULL) {
26973eeb121Sbostic cwarn("%s", out);
270782a8f7dSmckusick return;
271782a8f7dSmckusick }
272782a8f7dSmckusick
273a9024bf6Sbostic if ((ifp = zopen(in, "r", bits)) == NULL) {
27473eeb121Sbostic cwarn("%s", in);
275a9024bf6Sbostic goto err;
276782a8f7dSmckusick }
277a9024bf6Sbostic if (stat(in, &sb)) {
27873eeb121Sbostic cwarn("%s", in);
279a9024bf6Sbostic goto err;
280a9024bf6Sbostic }
281a902403dSbostic if (!S_ISREG(sb.st_mode))
282a902403dSbostic isreg = 0;
283782a8f7dSmckusick
284a9024bf6Sbostic while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
285a9024bf6Sbostic if (fwrite(buf, 1, nr, ofp) != nr) {
28673eeb121Sbostic cwarn("%s", out);
287a9024bf6Sbostic goto err;
288782a8f7dSmckusick }
289782a8f7dSmckusick
290a9024bf6Sbostic if (ferror(ifp) || fclose(ifp)) {
29173eeb121Sbostic cwarn("%s", in);
292a9024bf6Sbostic goto err;
293782a8f7dSmckusick }
294a9024bf6Sbostic ifp = NULL;
295782a8f7dSmckusick
296a9024bf6Sbostic if (fclose(ofp)) {
29773eeb121Sbostic cwarn("%s", out);
298a9024bf6Sbostic goto err;
299782a8f7dSmckusick }
300782a8f7dSmckusick
301a902403dSbostic if (isreg) {
302fcb61de9Sbostic setfile(out, &sb);
303fcb61de9Sbostic
304a902403dSbostic if (unlink(in))
30573eeb121Sbostic cwarn("%s", in);
306a902403dSbostic }
307a9024bf6Sbostic return;
308782a8f7dSmckusick
309a9024bf6Sbostic err: if (ofp) {
310a9024bf6Sbostic if (oreg)
311a9024bf6Sbostic (void)unlink(out);
312a9024bf6Sbostic (void)fclose(ofp);
313782a8f7dSmckusick }
314a9024bf6Sbostic if (ifp)
315a9024bf6Sbostic (void)fclose(ifp);
3166c8aaa2bSlepreau }
3176c8aaa2bSlepreau
318fcb61de9Sbostic void
setfile(name,fs)319fcb61de9Sbostic setfile(name, fs)
320fcb61de9Sbostic char *name;
321fcb61de9Sbostic register struct stat *fs;
322fcb61de9Sbostic {
323fcb61de9Sbostic static struct timeval tv[2];
324fcb61de9Sbostic
325fcb61de9Sbostic fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
326fcb61de9Sbostic
327fcb61de9Sbostic TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
328fcb61de9Sbostic TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
329fcb61de9Sbostic if (utimes(name, tv))
33073eeb121Sbostic cwarn("utimes: %s", name);
331fcb61de9Sbostic
332fcb61de9Sbostic /*
333fcb61de9Sbostic * Changing the ownership probably won't succeed, unless we're root
334fcb61de9Sbostic * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
335fcb61de9Sbostic * the mode; current BSD behavior is to remove all setuid bits on
336fcb61de9Sbostic * chown. If chown fails, lose setuid/setgid bits.
337fcb61de9Sbostic */
338fcb61de9Sbostic if (chown(name, fs->st_uid, fs->st_gid)) {
339fcb61de9Sbostic if (errno != EPERM)
34073eeb121Sbostic cwarn("chown: %s", name);
341fcb61de9Sbostic fs->st_mode &= ~(S_ISUID|S_ISGID);
342fcb61de9Sbostic }
343fcb61de9Sbostic if (chmod(name, fs->st_mode))
34473eeb121Sbostic cwarn("chown: %s", name);
345fcb61de9Sbostic
346fcb61de9Sbostic if (chflags(name, fs->st_flags))
34773eeb121Sbostic cwarn("chflags: %s", name);
348fcb61de9Sbostic }
349fcb61de9Sbostic
3506c8aaa2bSlepreau int
permission(fname)351a9024bf6Sbostic permission(fname)
352a9024bf6Sbostic char *fname;
3536c8aaa2bSlepreau {
354a9024bf6Sbostic int ch, first;
355782a8f7dSmckusick
356a9024bf6Sbostic if (!isatty(fileno(stderr)))
357a9024bf6Sbostic return (0);
358a9024bf6Sbostic (void)fprintf(stderr, "overwrite %s? ", fname);
359a9024bf6Sbostic first = ch = getchar();
360a9024bf6Sbostic while (ch != '\n' && ch != EOF)
361a9024bf6Sbostic ch = getchar();
362a9024bf6Sbostic return (first == 'y');
363782a8f7dSmckusick }
364782a8f7dSmckusick
365266ae1a1Sbostic void
usage(iscompress)366a9024bf6Sbostic usage(iscompress)
367a9024bf6Sbostic int iscompress;
368782a8f7dSmckusick {
369a9024bf6Sbostic if (iscompress)
3708c01a94eSbostic (void)fprintf(stderr,
371a9024bf6Sbostic "usage: compress [-cfv] [-b bits] [file ...]\n");
372a9024bf6Sbostic else
373a9024bf6Sbostic (void)fprintf(stderr,
374a9024bf6Sbostic "usage: uncompress [-c] [-b bits] [file ...]\n");
3758c01a94eSbostic exit(1);
3768c01a94eSbostic }
377a9024bf6Sbostic
378a9024bf6Sbostic void
379a9024bf6Sbostic #if __STDC__
cwarnx(const char * fmt,...)38073eeb121Sbostic cwarnx(const char *fmt, ...)
381a9024bf6Sbostic #else
38273eeb121Sbostic cwarnx(fmt, va_alist)
38373eeb121Sbostic int eval;
38473eeb121Sbostic const char *fmt;
385a9024bf6Sbostic va_dcl
386a9024bf6Sbostic #endif
387a9024bf6Sbostic {
388a9024bf6Sbostic va_list ap;
389a9024bf6Sbostic #if __STDC__
390a9024bf6Sbostic va_start(ap, fmt);
391a9024bf6Sbostic #else
392a9024bf6Sbostic va_start(ap);
393a9024bf6Sbostic #endif
39473eeb121Sbostic vwarnx(fmt, ap);
395a9024bf6Sbostic va_end(ap);
39673eeb121Sbostic eval = 1;
39773eeb121Sbostic }
39873eeb121Sbostic
39973eeb121Sbostic void
40073eeb121Sbostic #if __STDC__
cwarn(const char * fmt,...)40173eeb121Sbostic cwarn(const char *fmt, ...)
40273eeb121Sbostic #else
40373eeb121Sbostic cwarn(fmt, va_alist)
40473eeb121Sbostic int eval;
40573eeb121Sbostic const char *fmt;
40673eeb121Sbostic va_dcl
40773eeb121Sbostic #endif
40873eeb121Sbostic {
40973eeb121Sbostic va_list ap;
41073eeb121Sbostic #if __STDC__
41173eeb121Sbostic va_start(ap, fmt);
41273eeb121Sbostic #else
41373eeb121Sbostic va_start(ap);
41473eeb121Sbostic #endif
41573eeb121Sbostic vwarn(fmt, ap);
41673eeb121Sbostic va_end(ap);
417a9024bf6Sbostic eval = 1;
418a9024bf6Sbostic }
419