xref: /original-bsd/usr.bin/compress/compress.c (revision d3ed61d9)
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