compress.c revision 96772
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1992, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3541568Sarchiestatic const char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1992, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3887247Smarkm#endif
391590Srgrimes
4087628Sdwmalone#if 0
411590Srgrimes#ifndef lint
4287628Sdwmalonestatic char sccsid[] = "@(#)compress.c	8.2 (Berkeley) 1/7/94";
4358630Scharnier#endif
4487628Sdwmalone#endif
451590Srgrimes
4687628Sdwmalone#include <sys/cdefs.h>
4787628Sdwmalone__FBSDID("$FreeBSD: head/usr.bin/compress/compress.c 96772 2002-05-17 01:42:43Z tjr $");
4887628Sdwmalone
491590Srgrimes#include <sys/param.h>
501590Srgrimes#include <sys/stat.h>
5166907Swollman#include <sys/time.h>
521590Srgrimes
531590Srgrimes#include <err.h>
541590Srgrimes#include <errno.h>
5593055Simp#include <stdarg.h>
561590Srgrimes#include <stdio.h>
571590Srgrimes#include <stdlib.h>
581590Srgrimes#include <string.h>
591590Srgrimes#include <unistd.h>
601590Srgrimes
6118053Sbde#include "zopen.h"
6218053Sbde
6392920Simpvoid	compress(const char *, const char *, int);
6492920Simpvoid	cwarn(const char *, ...) __printflike(1, 2);
6592920Simpvoid	cwarnx(const char *, ...) __printflike(1, 2);
6692920Simpvoid	decompress(const char *, const char *, int);
6792920Simpint	permission(const char *);
6892920Simpvoid	setfile(const char *, struct stat *);
6992920Simpvoid	usage(int);
701590Srgrimes
711590Srgrimesint eval, force, verbose;
721590Srgrimes
731590Srgrimesint
741590Srgrimesmain(argc, argv)
751590Srgrimes	int argc;
761590Srgrimes	char *argv[];
771590Srgrimes{
7840547Sbde	enum {COMPRESS, DECOMPRESS} style;
791590Srgrimes	size_t len;
801590Srgrimes	int bits, cat, ch;
811590Srgrimes	char *p, newname[MAXPATHLEN];
821590Srgrimes
8340547Sbde	cat = 0;
841590Srgrimes	if ((p = rindex(argv[0], '/')) == NULL)
851590Srgrimes		p = argv[0];
861590Srgrimes	else
871590Srgrimes		++p;
881590Srgrimes	if (!strcmp(p, "uncompress"))
891590Srgrimes		style = DECOMPRESS;
908874Srgrimes	else if (!strcmp(p, "compress"))
911590Srgrimes		style = COMPRESS;
9240534Smsmith	else if (!strcmp(p, "zcat")) {
9340547Sbde		cat = 1;
9440534Smsmith		style = DECOMPRESS;
9540534Smsmith	} else
961590Srgrimes		errx(1, "unknown program name");
971590Srgrimes
9840547Sbde	bits = 0;
9924360Simp	while ((ch = getopt(argc, argv, "b:cdfv")) != -1)
1001590Srgrimes		switch(ch) {
1011590Srgrimes		case 'b':
1021590Srgrimes			bits = strtol(optarg, &p, 10);
1031590Srgrimes			if (*p)
1041590Srgrimes				errx(1, "illegal bit count -- %s", optarg);
1051590Srgrimes			break;
1061590Srgrimes		case 'c':
1071590Srgrimes			cat = 1;
1081590Srgrimes			break;
1091590Srgrimes		case 'd':		/* Backward compatible. */
1101590Srgrimes			style = DECOMPRESS;
1111590Srgrimes			break;
1121590Srgrimes		case 'f':
1131590Srgrimes			force = 1;
1141590Srgrimes			break;
1151590Srgrimes		case 'v':
1161590Srgrimes			verbose = 1;
1171590Srgrimes			break;
1181590Srgrimes		case '?':
1191590Srgrimes		default:
1201590Srgrimes			usage(style == COMPRESS);
1211590Srgrimes		}
1221590Srgrimes	argc -= optind;
1231590Srgrimes	argv += optind;
1241590Srgrimes
1251590Srgrimes	if (argc == 0) {
1261590Srgrimes		switch(style) {
1271590Srgrimes		case COMPRESS:
1281590Srgrimes			(void)compress("/dev/stdin", "/dev/stdout", bits);
1291590Srgrimes			break;
1301590Srgrimes		case DECOMPRESS:
1311590Srgrimes			(void)decompress("/dev/stdin", "/dev/stdout", bits);
1321590Srgrimes			break;
1331590Srgrimes		}
1341590Srgrimes		exit (eval);
1351590Srgrimes	}
1361590Srgrimes
1371590Srgrimes	if (cat == 1 && argc > 1)
1381590Srgrimes		errx(1, "the -c option permits only a single file argument");
1391590Srgrimes
1401590Srgrimes	for (; *argv; ++argv)
1411590Srgrimes		switch(style) {
1421590Srgrimes		case COMPRESS:
14396772Stjr			if (strcmp(*argv, "-") == 0) {
14496772Stjr				compress("/dev/stdin", "/dev/stdout", bits);
14596772Stjr				break;
14696772Stjr			} else if (cat) {
1471590Srgrimes				compress(*argv, "/dev/stdout", bits);
1481590Srgrimes				break;
1491590Srgrimes			}
1501590Srgrimes			if ((p = rindex(*argv, '.')) != NULL &&
1511590Srgrimes			    !strcmp(p, ".Z")) {
1521590Srgrimes				cwarnx("%s: name already has trailing .Z",
1531590Srgrimes				    *argv);
1541590Srgrimes				break;
1551590Srgrimes			}
1561590Srgrimes			len = strlen(*argv);
1571590Srgrimes			if (len > sizeof(newname) - 3) {
1581590Srgrimes				cwarnx("%s: name too long", *argv);
1591590Srgrimes				break;
1601590Srgrimes			}
1611590Srgrimes			memmove(newname, *argv, len);
1621590Srgrimes			newname[len] = '.';
1631590Srgrimes			newname[len + 1] = 'Z';
1641590Srgrimes			newname[len + 2] = '\0';
1651590Srgrimes			compress(*argv, newname, bits);
1661590Srgrimes			break;
1671590Srgrimes		case DECOMPRESS:
16896772Stjr			if (strcmp(*argv, "-") == 0) {
16996772Stjr				decompress("/dev/stdin", "/dev/stdout", bits);
17096772Stjr				break;
17196772Stjr			}
1721590Srgrimes			len = strlen(*argv);
1731590Srgrimes			if ((p = rindex(*argv, '.')) == NULL ||
1741590Srgrimes			    strcmp(p, ".Z")) {
1751590Srgrimes				if (len > sizeof(newname) - 3) {
1761590Srgrimes					cwarnx("%s: name too long", *argv);
1771590Srgrimes					break;
1781590Srgrimes				}
1791590Srgrimes				memmove(newname, *argv, len);
1801590Srgrimes				newname[len] = '.';
1811590Srgrimes				newname[len + 1] = 'Z';
1821590Srgrimes				newname[len + 2] = '\0';
1831590Srgrimes				decompress(newname,
1841590Srgrimes				    cat ? "/dev/stdout" : *argv, bits);
1851590Srgrimes			} else {
1861590Srgrimes				if (len - 2 > sizeof(newname) - 1) {
1871590Srgrimes					cwarnx("%s: name too long", *argv);
1881590Srgrimes					break;
1891590Srgrimes				}
1901590Srgrimes				memmove(newname, *argv, len - 2);
1911590Srgrimes				newname[len - 2] = '\0';
1921590Srgrimes				decompress(*argv,
1931590Srgrimes				    cat ? "/dev/stdout" : newname, bits);
1941590Srgrimes			}
1951590Srgrimes			break;
1961590Srgrimes		}
1971590Srgrimes	exit (eval);
1981590Srgrimes}
1991590Srgrimes
2001590Srgrimesvoid
2011590Srgrimescompress(in, out, bits)
20287214Smarkm	const char *in, *out;
2031590Srgrimes	int bits;
2041590Srgrimes{
20587214Smarkm	size_t nr;
2061590Srgrimes	struct stat isb, sb;
2071590Srgrimes	FILE *ifp, *ofp;
2081590Srgrimes	int exists, isreg, oreg;
2091590Srgrimes	u_char buf[1024];
2101590Srgrimes
2111590Srgrimes	exists = !stat(out, &sb);
2121590Srgrimes	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
2131590Srgrimes		return;
2141590Srgrimes	isreg = oreg = !exists || S_ISREG(sb.st_mode);
2151590Srgrimes
2161590Srgrimes	ifp = ofp = NULL;
2171590Srgrimes	if ((ifp = fopen(in, "r")) == NULL) {
2181590Srgrimes		cwarn("%s", in);
2191590Srgrimes		return;
2201590Srgrimes	}
2211590Srgrimes	if (stat(in, &isb)) {		/* DON'T FSTAT! */
2221590Srgrimes		cwarn("%s", in);
2231590Srgrimes		goto err;
2241590Srgrimes	}
2251590Srgrimes	if (!S_ISREG(isb.st_mode))
2261590Srgrimes		isreg = 0;
2271590Srgrimes
2281590Srgrimes	if ((ofp = zopen(out, "w", bits)) == NULL) {
2291590Srgrimes		cwarn("%s", out);
2301590Srgrimes		goto err;
2311590Srgrimes	}
2321590Srgrimes	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
2331590Srgrimes		if (fwrite(buf, 1, nr, ofp) != nr) {
2341590Srgrimes			cwarn("%s", out);
2351590Srgrimes			goto err;
2361590Srgrimes		}
2371590Srgrimes
2381590Srgrimes	if (ferror(ifp) || fclose(ifp)) {
2391590Srgrimes		cwarn("%s", in);
2401590Srgrimes		goto err;
2411590Srgrimes	}
2421590Srgrimes	ifp = NULL;
2431590Srgrimes
2441590Srgrimes	if (fclose(ofp)) {
2451590Srgrimes		cwarn("%s", out);
2461590Srgrimes		goto err;
2471590Srgrimes	}
2481590Srgrimes	ofp = NULL;
2491590Srgrimes
2501590Srgrimes	if (isreg) {
2511590Srgrimes		if (stat(out, &sb)) {
2521590Srgrimes			cwarn("%s", out);
2531590Srgrimes			goto err;
2541590Srgrimes		}
2551590Srgrimes
2561590Srgrimes		if (!force && sb.st_size >= isb.st_size) {
2571590Srgrimes			if (verbose)
25896770Stjr		(void)fprintf(stderr, "%s: file would grow; left unmodified\n",
25996770Stjr		    in);
26096769Stjr			eval = 2;
2611590Srgrimes			if (unlink(out))
2621590Srgrimes				cwarn("%s", out);
2631590Srgrimes			goto err;
2641590Srgrimes		}
2651590Srgrimes
2661590Srgrimes		setfile(out, &isb);
2671590Srgrimes
2681590Srgrimes		if (unlink(in))
2691590Srgrimes			cwarn("%s", in);
2701590Srgrimes
2711590Srgrimes		if (verbose) {
27296770Stjr			(void)fprintf(stderr, "%s: ", out);
2731590Srgrimes			if (isb.st_size > sb.st_size)
27496770Stjr				(void)fprintf(stderr, "%.0f%% compression\n",
2751590Srgrimes				    ((float)sb.st_size / isb.st_size) * 100.0);
2761590Srgrimes			else
27796770Stjr				(void)fprintf(stderr, "%.0f%% expansion\n",
2781590Srgrimes				    ((float)isb.st_size / sb.st_size) * 100.0);
2791590Srgrimes		}
2801590Srgrimes	}
2811590Srgrimes	return;
2821590Srgrimes
2831590Srgrimeserr:	if (ofp) {
2841590Srgrimes		if (oreg)
2851590Srgrimes			(void)unlink(out);
2861590Srgrimes		(void)fclose(ofp);
2871590Srgrimes	}
2881590Srgrimes	if (ifp)
2891590Srgrimes		(void)fclose(ifp);
2901590Srgrimes}
2911590Srgrimes
2921590Srgrimesvoid
2931590Srgrimesdecompress(in, out, bits)
29487214Smarkm	const char *in, *out;
2951590Srgrimes	int bits;
2961590Srgrimes{
29787214Smarkm	size_t nr;
2981590Srgrimes	struct stat sb;
2991590Srgrimes	FILE *ifp, *ofp;
3001590Srgrimes	int exists, isreg, oreg;
3011590Srgrimes	u_char buf[1024];
3021590Srgrimes
3031590Srgrimes	exists = !stat(out, &sb);
3041590Srgrimes	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
3051590Srgrimes		return;
3061590Srgrimes	isreg = oreg = !exists || S_ISREG(sb.st_mode);
3071590Srgrimes
3081590Srgrimes	ifp = ofp = NULL;
3091590Srgrimes	if ((ofp = fopen(out, "w")) == NULL) {
3101590Srgrimes		cwarn("%s", out);
3111590Srgrimes		return;
3121590Srgrimes	}
3131590Srgrimes
3141590Srgrimes	if ((ifp = zopen(in, "r", bits)) == NULL) {
3151590Srgrimes		cwarn("%s", in);
3161590Srgrimes		goto err;
3171590Srgrimes	}
3181590Srgrimes	if (stat(in, &sb)) {
3191590Srgrimes		cwarn("%s", in);
3201590Srgrimes		goto err;
3211590Srgrimes	}
3221590Srgrimes	if (!S_ISREG(sb.st_mode))
3231590Srgrimes		isreg = 0;
3241590Srgrimes
3251590Srgrimes	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
3261590Srgrimes		if (fwrite(buf, 1, nr, ofp) != nr) {
3271590Srgrimes			cwarn("%s", out);
3281590Srgrimes			goto err;
3291590Srgrimes		}
3301590Srgrimes
3311590Srgrimes	if (ferror(ifp) || fclose(ifp)) {
3321590Srgrimes		cwarn("%s", in);
3331590Srgrimes		goto err;
3341590Srgrimes	}
3351590Srgrimes	ifp = NULL;
3361590Srgrimes
3371590Srgrimes	if (fclose(ofp)) {
3381590Srgrimes		cwarn("%s", out);
3391590Srgrimes		goto err;
3401590Srgrimes	}
3411590Srgrimes
3421590Srgrimes	if (isreg) {
3431590Srgrimes		setfile(out, &sb);
3441590Srgrimes
3451590Srgrimes		if (unlink(in))
3461590Srgrimes			cwarn("%s", in);
3471590Srgrimes	}
3481590Srgrimes	return;
3491590Srgrimes
3501590Srgrimeserr:	if (ofp) {
3511590Srgrimes		if (oreg)
3521590Srgrimes			(void)unlink(out);
3531590Srgrimes		(void)fclose(ofp);
3541590Srgrimes	}
3551590Srgrimes	if (ifp)
3561590Srgrimes		(void)fclose(ifp);
3571590Srgrimes}
3581590Srgrimes
3591590Srgrimesvoid
3601590Srgrimessetfile(name, fs)
36187214Smarkm	const char *name;
36287214Smarkm	struct stat *fs;
3631590Srgrimes{
3641590Srgrimes	static struct timeval tv[2];
3651590Srgrimes
3661590Srgrimes	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
3671590Srgrimes
3681590Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
3691590Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
3701590Srgrimes	if (utimes(name, tv))
3711590Srgrimes		cwarn("utimes: %s", name);
3721590Srgrimes
3731590Srgrimes	/*
3741590Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
3751590Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
3761590Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
3771590Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
3781590Srgrimes	 */
3791590Srgrimes	if (chown(name, fs->st_uid, fs->st_gid)) {
3801590Srgrimes		if (errno != EPERM)
3811590Srgrimes			cwarn("chown: %s", name);
3821590Srgrimes		fs->st_mode &= ~(S_ISUID|S_ISGID);
3831590Srgrimes	}
38460622Shoek	if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP)
38558630Scharnier		cwarn("chmod: %s", name);
3861590Srgrimes
38760622Shoek	if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP)
3881590Srgrimes		cwarn("chflags: %s", name);
3891590Srgrimes}
3901590Srgrimes
3911590Srgrimesint
3921590Srgrimespermission(fname)
39387214Smarkm	const char *fname;
3941590Srgrimes{
3951590Srgrimes	int ch, first;
3961590Srgrimes
3971590Srgrimes	if (!isatty(fileno(stderr)))
3981590Srgrimes		return (0);
3991590Srgrimes	(void)fprintf(stderr, "overwrite %s? ", fname);
4001590Srgrimes	first = ch = getchar();
4011590Srgrimes	while (ch != '\n' && ch != EOF)
4021590Srgrimes		ch = getchar();
4031590Srgrimes	return (first == 'y');
4041590Srgrimes}
4051590Srgrimes
4061590Srgrimesvoid
4071590Srgrimesusage(iscompress)
4081590Srgrimes	int iscompress;
4091590Srgrimes{
4101590Srgrimes	if (iscompress)
4111590Srgrimes		(void)fprintf(stderr,
4121590Srgrimes		    "usage: compress [-cfv] [-b bits] [file ...]\n");
4131590Srgrimes	else
4141590Srgrimes		(void)fprintf(stderr,
4151590Srgrimes		    "usage: uncompress [-c] [-b bits] [file ...]\n");
4161590Srgrimes	exit(1);
4171590Srgrimes}
4181590Srgrimes
4191590Srgrimesvoid
4201590Srgrimescwarnx(const char *fmt, ...)
4211590Srgrimes{
4221590Srgrimes	va_list ap;
42393055Simp
4241590Srgrimes	va_start(ap, fmt);
4251590Srgrimes	vwarnx(fmt, ap);
4261590Srgrimes	va_end(ap);
4271590Srgrimes	eval = 1;
4281590Srgrimes}
4291590Srgrimes
4301590Srgrimesvoid
4311590Srgrimescwarn(const char *fmt, ...)
4321590Srgrimes{
4331590Srgrimes	va_list ap;
43493055Simp
4351590Srgrimes	va_start(ap, fmt);
4361590Srgrimes	vwarn(fmt, ap);
4371590Srgrimes	va_end(ap);
4381590Srgrimes	eval = 1;
4391590Srgrimes}
440