xref: /csrg-svn/usr.bin/compress/compress.c (revision 65562)
156969Sbostic /*-
261954Sbostic  * Copyright (c) 1992, 1993
361954Sbostic  *	The Regents of the University of California.  All rights reserved.
435808Sbostic  *
542724Sbostic  * %sccs.include.redist.c%
635808Sbostic  */
735808Sbostic 
822592Smckusick #ifndef lint
961954Sbostic static char copyright[] =
1061954Sbostic "@(#) Copyright (c) 1992, 1993\n\
1161954Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1235808Sbostic #endif /* not lint */
1322592Smckusick 
1435808Sbostic #ifndef lint
15*65562Sbostic static char sccsid[] = "@(#)compress.c	8.2 (Berkeley) 01/07/94";
1635808Sbostic #endif /* not lint */
1735808Sbostic 
1847441Sbostic #include <sys/param.h>
1956969Sbostic #include <sys/time.h>
2047441Sbostic #include <sys/stat.h>
2156969Sbostic 
2259419Sbostic #include <err.h>
2347444Sbostic #include <errno.h>
2447441Sbostic #include <stdio.h>
2547441Sbostic #include <stdlib.h>
2647441Sbostic #include <string.h>
2756969Sbostic #include <unistd.h>
2847441Sbostic 
2959419Sbostic #ifdef __STDC__
3059419Sbostic #include <stdarg.h>
3159419Sbostic #else
3259419Sbostic #include <varargs.h>
3359419Sbostic #endif
3459419Sbostic 
3556976Sbostic void	compress __P((char *, char *, int));
3659419Sbostic void	cwarn __P((const char *, ...));
3759419Sbostic void	cwarnx __P((const char *, ...));
3856976Sbostic void	decompress __P((char *, char *, int));
3956976Sbostic int	permission __P((char *));
4056976Sbostic void	setfile __P((char *, struct stat *));
4156976Sbostic void	usage __P((int));
4222592Smckusick 
4356969Sbostic int eval, force, verbose;
4422730Smckusick 
4556969Sbostic int
main(argc,argv)4647441Sbostic main(argc, argv)
4747441Sbostic 	int argc;
4856969Sbostic 	char *argv[];
4922592Smckusick {
5056969Sbostic 	enum {COMPRESS, DECOMPRESS} style;
5156969Sbostic 	size_t len;
5256969Sbostic 	int bits, cat, ch;
5356969Sbostic 	char *p, newname[MAXPATHLEN];
5422592Smckusick 
5556969Sbostic 	if ((p = rindex(argv[0], '/')) == NULL)
5656969Sbostic 		p = argv[0];
5747441Sbostic 	else
5856969Sbostic 		++p;
5959425Sbostic 	if (!strcmp(p, "uncompress"))
6056969Sbostic 		style = DECOMPRESS;
6159425Sbostic 	else if (!strcmp(p, "compress"))
6256969Sbostic 		style = COMPRESS;
6359425Sbostic 	else
6459419Sbostic 		errx(1, "unknown program name");
6522592Smckusick 
6656969Sbostic 	bits = cat = 0;
6756969Sbostic 	while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
6847441Sbostic 		switch(ch) {
6947441Sbostic 		case 'b':
7056969Sbostic 			bits = strtol(optarg, &p, 10);
7156969Sbostic 			if (*p)
7259419Sbostic 				errx(1, "illegal bit count -- %s", optarg);
7347441Sbostic 			break;
7447441Sbostic 		case 'c':
7556969Sbostic 			cat = 1;
7647441Sbostic 			break;
7756969Sbostic 		case 'd':		/* Backward compatible. */
7856969Sbostic 			style = DECOMPRESS;
7922592Smckusick 			break;
8047441Sbostic 		case 'f':
8122730Smckusick 			force = 1;
8222592Smckusick 			break;
8356969Sbostic 		case 'v':
8447441Sbostic 			verbose = 1;
8522592Smckusick 			break;
8647441Sbostic 		case '?':
8747441Sbostic 		default:
8856969Sbostic 			usage(style == COMPRESS);
8922592Smckusick 		}
9047441Sbostic 	argc -= optind;
9147441Sbostic 	argv += optind;
9222592Smckusick 
9356969Sbostic 	if (argc == 0) {
9456969Sbostic 		switch(style) {
9556969Sbostic 		case COMPRESS:
9656969Sbostic 			(void)compress("/dev/stdin", "/dev/stdout", bits);
9756969Sbostic 			break;
9856969Sbostic 		case DECOMPRESS:
9956969Sbostic 			(void)decompress("/dev/stdin", "/dev/stdout", bits);
10056969Sbostic 			break;
10156969Sbostic 		}
10256969Sbostic 		exit (eval);
10356969Sbostic 	}
10422592Smckusick 
10556969Sbostic 	if (cat == 1 && argc > 1)
10659419Sbostic 		errx(1, "the -c option permits only a single file argument");
10747441Sbostic 
10856969Sbostic 	for (; *argv; ++argv)
10956969Sbostic 		switch(style) {
11056969Sbostic 		case COMPRESS:
11156969Sbostic 			if (cat) {
11256969Sbostic 				compress(*argv, "/dev/stdout", bits);
11356969Sbostic 				break;
11422592Smckusick 			}
11556969Sbostic 			if ((p = rindex(*argv, '.')) != NULL &&
11656969Sbostic 			    !strcmp(p, ".Z")) {
11759419Sbostic 				cwarnx("%s: name already has trailing .Z",
11856969Sbostic 				    *argv);
11956969Sbostic 				break;
12056969Sbostic 			}
12156969Sbostic 			len = strlen(*argv);
12256969Sbostic 			if (len > sizeof(newname) - 3) {
12359419Sbostic 				cwarnx("%s: name too long", *argv);
12456969Sbostic 				break;
12556969Sbostic 			}
12656969Sbostic 			memmove(newname, *argv, len);
12756969Sbostic 			newname[len] = '.';
12856969Sbostic 			newname[len + 1] = 'Z';
12956969Sbostic 			newname[len + 2] = '\0';
13056969Sbostic 			compress(*argv, newname, bits);
13156969Sbostic 			break;
13256969Sbostic 		case DECOMPRESS:
13356969Sbostic 			len = strlen(*argv);
13456976Sbostic 			if ((p = rindex(*argv, '.')) == NULL ||
13556976Sbostic 			    strcmp(p, ".Z")) {
13656969Sbostic 				if (len > sizeof(newname) - 3) {
13759419Sbostic 					cwarnx("%s: name too long", *argv);
13856969Sbostic 					break;
13956969Sbostic 				}
14056969Sbostic 				memmove(newname, *argv, len);
14156969Sbostic 				newname[len] = '.';
14256969Sbostic 				newname[len + 1] = 'Z';
14356969Sbostic 				newname[len + 2] = '\0';
14456969Sbostic 				decompress(newname,
14556969Sbostic 				    cat ? "/dev/stdout" : *argv, bits);
14656969Sbostic 			} else {
14756969Sbostic 				if (len - 2 > sizeof(newname) - 1) {
14859419Sbostic 					cwarnx("%s: name too long", *argv);
14956969Sbostic 					break;
15056969Sbostic 				}
15156969Sbostic 				memmove(newname, *argv, len - 2);
15256969Sbostic 				newname[len - 2] = '\0';
15356969Sbostic 				decompress(*argv,
15456969Sbostic 				    cat ? "/dev/stdout" : newname, bits);
15556969Sbostic 			}
15656969Sbostic 			break;
15722592Smckusick 		}
15856969Sbostic 	exit (eval);
15922592Smckusick }
16022592Smckusick 
16156969Sbostic void
compress(in,out,bits)16256969Sbostic compress(in, out, bits)
16356969Sbostic 	char *in, *out;
16456969Sbostic 	int bits;
16547444Sbostic {
16656969Sbostic 	register int nr;
16756976Sbostic 	struct stat isb, sb;
16856969Sbostic 	FILE *ifp, *ofp;
16956973Sbostic 	int exists, isreg, oreg;
17056969Sbostic 	u_char buf[1024];
17122592Smckusick 
17256969Sbostic 	exists = !stat(out, &sb);
17356969Sbostic 	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
17456969Sbostic 		return;
17556973Sbostic 	isreg = oreg = !exists || S_ISREG(sb.st_mode);
17622592Smckusick 
17756969Sbostic 	ifp = ofp = NULL;
17856969Sbostic 	if ((ifp = fopen(in, "r")) == NULL) {
17959419Sbostic 		cwarn("%s", in);
18056969Sbostic 		return;
18122592Smckusick 	}
18256976Sbostic 	if (stat(in, &isb)) {		/* DON'T FSTAT! */
18359419Sbostic 		cwarn("%s", in);
18456969Sbostic 		goto err;
18522592Smckusick 	}
18656976Sbostic 	if (!S_ISREG(isb.st_mode))
18756973Sbostic 		isreg = 0;
18822592Smckusick 
18956969Sbostic 	if ((ofp = zopen(out, "w", bits)) == NULL) {
19059419Sbostic 		cwarn("%s", out);
19156969Sbostic 		goto err;
19256969Sbostic 	}
19356969Sbostic 	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
19456969Sbostic 		if (fwrite(buf, 1, nr, ofp) != nr) {
19559419Sbostic 			cwarn("%s", out);
19656969Sbostic 			goto err;
19756969Sbostic 		}
19822592Smckusick 
19956969Sbostic 	if (ferror(ifp) || fclose(ifp)) {
20059419Sbostic 		cwarn("%s", in);
20156969Sbostic 		goto err;
20256969Sbostic 	}
20356969Sbostic 	ifp = NULL;
20422592Smckusick 
20556969Sbostic 	if (fclose(ofp)) {
20659419Sbostic 		cwarn("%s", out);
20756969Sbostic 		goto err;
20856969Sbostic 	}
20956969Sbostic 	ofp = NULL;
21022592Smckusick 
21156973Sbostic 	if (isreg) {
21256969Sbostic 		if (stat(out, &sb)) {
21359419Sbostic 			cwarn("%s", out);
21456969Sbostic 			goto err;
21556969Sbostic 		}
21656973Sbostic 
21756976Sbostic 		if (!force && sb.st_size >= isb.st_size) {
21856969Sbostic 			if (verbose)
21956969Sbostic 		(void)printf("%s: file would grow; left unmodified\n", in);
22056969Sbostic 			if (unlink(out))
22159419Sbostic 				cwarn("%s", out);
22256969Sbostic 			goto err;
22356969Sbostic 		}
22422592Smckusick 
22556976Sbostic 		setfile(out, &isb);
22656976Sbostic 
22756973Sbostic 		if (unlink(in))
22859419Sbostic 			cwarn("%s", in);
22922592Smckusick 
23056973Sbostic 		if (verbose) {
23156973Sbostic 			(void)printf("%s: ", out);
23256976Sbostic 			if (isb.st_size > sb.st_size)
23356973Sbostic 				(void)printf("%.0f%% compression\n",
23456976Sbostic 				    ((float)sb.st_size / isb.st_size) * 100.0);
23556973Sbostic 			else
23656973Sbostic 				(void)printf("%.0f%% expansion\n",
23756976Sbostic 				    ((float)isb.st_size / sb.st_size) * 100.0);
23856973Sbostic 		}
23922592Smckusick 	}
24056969Sbostic 	return;
24122592Smckusick 
24256969Sbostic err:	if (ofp) {
24356969Sbostic 		if (oreg)
24456969Sbostic 			(void)unlink(out);
24556969Sbostic 		(void)fclose(ofp);
24622592Smckusick 	}
24756969Sbostic 	if (ifp)
24856969Sbostic 		(void)fclose(ifp);
24922592Smckusick }
25022592Smckusick 
25156969Sbostic void
decompress(in,out,bits)25256969Sbostic decompress(in, out, bits)
25356969Sbostic 	char *in, *out;
25456969Sbostic 	int bits;
25556969Sbostic {
25656969Sbostic 	register int nr;
25756969Sbostic 	struct stat sb;
25856969Sbostic 	FILE *ifp, *ofp;
25956976Sbostic 	int exists, isreg, oreg;
26056969Sbostic 	u_char buf[1024];
26122730Smckusick 
26256976Sbostic 	exists = !stat(out, &sb);
26356976Sbostic 	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
26456969Sbostic 		return;
26556976Sbostic 	isreg = oreg = !exists || S_ISREG(sb.st_mode);
26622592Smckusick 
26756969Sbostic 	ifp = ofp = NULL;
26856969Sbostic 	if ((ofp = fopen(out, "w")) == NULL) {
26959419Sbostic 		cwarn("%s", out);
27056969Sbostic 		return;
27156969Sbostic 	}
27247431Sbostic 
27356969Sbostic 	if ((ifp = zopen(in, "r", bits)) == NULL) {
27459419Sbostic 		cwarn("%s", in);
27556969Sbostic 		goto err;
27622592Smckusick 	}
27756969Sbostic 	if (stat(in, &sb)) {
27859419Sbostic 		cwarn("%s", in);
27956969Sbostic 		goto err;
28022592Smckusick 	}
28156973Sbostic 	if (!S_ISREG(sb.st_mode))
28256973Sbostic 		isreg = 0;
28322592Smckusick 
28456969Sbostic 	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
28556969Sbostic 		if (fwrite(buf, 1, nr, ofp) != nr) {
28659419Sbostic 			cwarn("%s", out);
28756969Sbostic 			goto err;
28856969Sbostic 		}
28922592Smckusick 
29056969Sbostic 	if (ferror(ifp) || fclose(ifp)) {
29159419Sbostic 		cwarn("%s", in);
29256969Sbostic 		goto err;
29322592Smckusick 	}
29456969Sbostic 	ifp = NULL;
29522592Smckusick 
29656969Sbostic 	if (fclose(ofp)) {
29759419Sbostic 		cwarn("%s", out);
29856969Sbostic 		goto err;
29922592Smckusick 	}
30022592Smckusick 
30156973Sbostic 	if (isreg) {
30256976Sbostic 		setfile(out, &sb);
30356976Sbostic 
30456973Sbostic 		if (unlink(in))
30559419Sbostic 			cwarn("%s", in);
30656973Sbostic 	}
30756969Sbostic 	return;
30824830Slepreau 
30956969Sbostic err:	if (ofp) {
31056969Sbostic 		if (oreg)
31156969Sbostic 			(void)unlink(out);
31256969Sbostic 		(void)fclose(ofp);
31322592Smckusick 	}
31456969Sbostic 	if (ifp)
31556969Sbostic 		(void)fclose(ifp);
31622592Smckusick }
31722592Smckusick 
31856976Sbostic void
setfile(name,fs)31956976Sbostic setfile(name, fs)
32056976Sbostic 	char *name;
32156976Sbostic 	register struct stat *fs;
32256976Sbostic {
32356976Sbostic 	static struct timeval tv[2];
32456976Sbostic 
32556976Sbostic 	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
32656976Sbostic 
32756976Sbostic 	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
32856976Sbostic 	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
32956976Sbostic 	if (utimes(name, tv))
33059419Sbostic 		cwarn("utimes: %s", name);
33156976Sbostic 
33256976Sbostic 	/*
33356976Sbostic 	 * Changing the ownership probably won't succeed, unless we're root
33456976Sbostic 	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
33556976Sbostic 	 * the mode; current BSD behavior is to remove all setuid bits on
33656976Sbostic 	 * chown.  If chown fails, lose setuid/setgid bits.
33756976Sbostic 	 */
33856976Sbostic 	if (chown(name, fs->st_uid, fs->st_gid)) {
33956976Sbostic 		if (errno != EPERM)
34059419Sbostic 			cwarn("chown: %s", name);
34156976Sbostic 		fs->st_mode &= ~(S_ISUID|S_ISGID);
34256976Sbostic 	}
34356976Sbostic 	if (chmod(name, fs->st_mode))
34459419Sbostic 		cwarn("chown: %s", name);
34556976Sbostic 
34656976Sbostic 	if (chflags(name, fs->st_flags))
34759419Sbostic 		cwarn("chflags: %s", name);
34856976Sbostic }
34956976Sbostic 
35024830Slepreau int
permission(fname)35156969Sbostic permission(fname)
35256969Sbostic 	char *fname;
35324830Slepreau {
35456969Sbostic 	int ch, first;
35556969Sbostic 
35656969Sbostic 	if (!isatty(fileno(stderr)))
35756969Sbostic 		return (0);
35856969Sbostic 	(void)fprintf(stderr, "overwrite %s? ", fname);
35956969Sbostic 	first = ch = getchar();
36056969Sbostic 	while (ch != '\n' && ch != EOF)
36156969Sbostic 		ch = getchar();
36256969Sbostic 	return (first == 'y');
36324830Slepreau }
36422592Smckusick 
36556969Sbostic void
usage(iscompress)36656969Sbostic usage(iscompress)
36756969Sbostic 	int iscompress;
36822592Smckusick {
36956969Sbostic 	if (iscompress)
37056969Sbostic 		(void)fprintf(stderr,
37156969Sbostic 		    "usage: compress [-cfv] [-b bits] [file ...]\n");
37256969Sbostic 	else
37356969Sbostic 		(void)fprintf(stderr,
37456969Sbostic 		    "usage: uncompress [-c] [-b bits] [file ...]\n");
37547444Sbostic 	exit(1);
37622592Smckusick }
37722592Smckusick 
37859419Sbostic void
37956969Sbostic #if __STDC__
cwarnx(const char * fmt,...)38059419Sbostic cwarnx(const char *fmt, ...)
38156969Sbostic #else
38259419Sbostic cwarnx(fmt, va_alist)
38359419Sbostic 	int eval;
38459419Sbostic 	const char *fmt;
38559419Sbostic 	va_dcl
38656969Sbostic #endif
38759419Sbostic {
38859419Sbostic 	va_list ap;
38959419Sbostic #if __STDC__
39059419Sbostic 	va_start(ap, fmt);
39159419Sbostic #else
39259419Sbostic 	va_start(ap);
39359419Sbostic #endif
39459419Sbostic 	vwarnx(fmt, ap);
39559419Sbostic 	va_end(ap);
39659419Sbostic 	eval = 1;
39759419Sbostic }
39822592Smckusick 
39939167Sbostic void
40056969Sbostic #if __STDC__
cwarn(const char * fmt,...)40159419Sbostic cwarn(const char *fmt, ...)
40256969Sbostic #else
40359419Sbostic cwarn(fmt, va_alist)
40459419Sbostic 	int eval;
40559419Sbostic 	const char *fmt;
40656969Sbostic 	va_dcl
40724830Slepreau #endif
40822592Smckusick {
40956969Sbostic 	va_list ap;
41056969Sbostic #if __STDC__
41156969Sbostic 	va_start(ap, fmt);
41247441Sbostic #else
41356969Sbostic 	va_start(ap);
41447441Sbostic #endif
41559419Sbostic 	vwarn(fmt, ap);
41656969Sbostic 	va_end(ap);
41756969Sbostic 	eval = 1;
41847441Sbostic }
419