compress.c revision 87214
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";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
4158630Scharnier#if 0
4258630Scharnierstatic char sccsid[] = "@(#)compress.c	8.2 (Berkeley) 1/7/94";
4358630Scharnier#endif
4458630Scharnierstatic const char rcsid[] =
4558630Scharnier  "$FreeBSD: head/usr.bin/compress/compress.c 87214 2001-12-02 13:31:22Z markm $";
461590Srgrimes#endif /* not lint */
471590Srgrimes
481590Srgrimes#include <sys/param.h>
491590Srgrimes#include <sys/stat.h>
5066907Swollman#include <sys/time.h>
511590Srgrimes
521590Srgrimes#include <err.h>
531590Srgrimes#include <errno.h>
541590Srgrimes#include <stdio.h>
551590Srgrimes#include <stdlib.h>
561590Srgrimes#include <string.h>
571590Srgrimes#include <unistd.h>
581590Srgrimes
591590Srgrimes#ifdef __STDC__
601590Srgrimes#include <stdarg.h>
611590Srgrimes#else
621590Srgrimes#include <varargs.h>
631590Srgrimes#endif
641590Srgrimes
6518053Sbde#include "zopen.h"
6618053Sbde
6787214Smarkmvoid	compress __P((const char *, const char *, int));
6879305Skrisvoid	cwarn __P((const char *, ...)) __printflike(1, 2);
6979305Skrisvoid	cwarnx __P((const char *, ...)) __printflike(1, 2);
7087214Smarkmvoid	decompress __P((const char *, const char *, int));
7187214Smarkmint	permission __P((const char *));
7287214Smarkmvoid	setfile __P((const char *, struct stat *));
731590Srgrimesvoid	usage __P((int));
741590Srgrimes
751590Srgrimesint eval, force, verbose;
761590Srgrimes
771590Srgrimesint
781590Srgrimesmain(argc, argv)
791590Srgrimes	int argc;
801590Srgrimes	char *argv[];
811590Srgrimes{
8240547Sbde	enum {COMPRESS, DECOMPRESS} style;
831590Srgrimes	size_t len;
841590Srgrimes	int bits, cat, ch;
851590Srgrimes	char *p, newname[MAXPATHLEN];
861590Srgrimes
8740547Sbde	cat = 0;
881590Srgrimes	if ((p = rindex(argv[0], '/')) == NULL)
891590Srgrimes		p = argv[0];
901590Srgrimes	else
911590Srgrimes		++p;
921590Srgrimes	if (!strcmp(p, "uncompress"))
931590Srgrimes		style = DECOMPRESS;
948874Srgrimes	else if (!strcmp(p, "compress"))
951590Srgrimes		style = COMPRESS;
9640534Smsmith	else if (!strcmp(p, "zcat")) {
9740547Sbde		cat = 1;
9840534Smsmith		style = DECOMPRESS;
9940534Smsmith	} else
1001590Srgrimes		errx(1, "unknown program name");
1011590Srgrimes
10240547Sbde	bits = 0;
10324360Simp	while ((ch = getopt(argc, argv, "b:cdfv")) != -1)
1041590Srgrimes		switch(ch) {
1051590Srgrimes		case 'b':
1061590Srgrimes			bits = strtol(optarg, &p, 10);
1071590Srgrimes			if (*p)
1081590Srgrimes				errx(1, "illegal bit count -- %s", optarg);
1091590Srgrimes			break;
1101590Srgrimes		case 'c':
1111590Srgrimes			cat = 1;
1121590Srgrimes			break;
1131590Srgrimes		case 'd':		/* Backward compatible. */
1141590Srgrimes			style = DECOMPRESS;
1151590Srgrimes			break;
1161590Srgrimes		case 'f':
1171590Srgrimes			force = 1;
1181590Srgrimes			break;
1191590Srgrimes		case 'v':
1201590Srgrimes			verbose = 1;
1211590Srgrimes			break;
1221590Srgrimes		case '?':
1231590Srgrimes		default:
1241590Srgrimes			usage(style == COMPRESS);
1251590Srgrimes		}
1261590Srgrimes	argc -= optind;
1271590Srgrimes	argv += optind;
1281590Srgrimes
1291590Srgrimes	if (argc == 0) {
1301590Srgrimes		switch(style) {
1311590Srgrimes		case COMPRESS:
1321590Srgrimes			(void)compress("/dev/stdin", "/dev/stdout", bits);
1331590Srgrimes			break;
1341590Srgrimes		case DECOMPRESS:
1351590Srgrimes			(void)decompress("/dev/stdin", "/dev/stdout", bits);
1361590Srgrimes			break;
1371590Srgrimes		}
1381590Srgrimes		exit (eval);
1391590Srgrimes	}
1401590Srgrimes
1411590Srgrimes	if (cat == 1 && argc > 1)
1421590Srgrimes		errx(1, "the -c option permits only a single file argument");
1431590Srgrimes
1441590Srgrimes	for (; *argv; ++argv)
1451590Srgrimes		switch(style) {
1461590Srgrimes		case COMPRESS:
1471590Srgrimes			if (cat) {
1481590Srgrimes				compress(*argv, "/dev/stdout", bits);
1491590Srgrimes				break;
1501590Srgrimes			}
1511590Srgrimes			if ((p = rindex(*argv, '.')) != NULL &&
1521590Srgrimes			    !strcmp(p, ".Z")) {
1531590Srgrimes				cwarnx("%s: name already has trailing .Z",
1541590Srgrimes				    *argv);
1551590Srgrimes				break;
1561590Srgrimes			}
1571590Srgrimes			len = strlen(*argv);
1581590Srgrimes			if (len > sizeof(newname) - 3) {
1591590Srgrimes				cwarnx("%s: name too long", *argv);
1601590Srgrimes				break;
1611590Srgrimes			}
1621590Srgrimes			memmove(newname, *argv, len);
1631590Srgrimes			newname[len] = '.';
1641590Srgrimes			newname[len + 1] = 'Z';
1651590Srgrimes			newname[len + 2] = '\0';
1661590Srgrimes			compress(*argv, newname, bits);
1671590Srgrimes			break;
1681590Srgrimes		case DECOMPRESS:
1691590Srgrimes			len = strlen(*argv);
1701590Srgrimes			if ((p = rindex(*argv, '.')) == NULL ||
1711590Srgrimes			    strcmp(p, ".Z")) {
1721590Srgrimes				if (len > sizeof(newname) - 3) {
1731590Srgrimes					cwarnx("%s: name too long", *argv);
1741590Srgrimes					break;
1751590Srgrimes				}
1761590Srgrimes				memmove(newname, *argv, len);
1771590Srgrimes				newname[len] = '.';
1781590Srgrimes				newname[len + 1] = 'Z';
1791590Srgrimes				newname[len + 2] = '\0';
1801590Srgrimes				decompress(newname,
1811590Srgrimes				    cat ? "/dev/stdout" : *argv, bits);
1821590Srgrimes			} else {
1831590Srgrimes				if (len - 2 > sizeof(newname) - 1) {
1841590Srgrimes					cwarnx("%s: name too long", *argv);
1851590Srgrimes					break;
1861590Srgrimes				}
1871590Srgrimes				memmove(newname, *argv, len - 2);
1881590Srgrimes				newname[len - 2] = '\0';
1891590Srgrimes				decompress(*argv,
1901590Srgrimes				    cat ? "/dev/stdout" : newname, bits);
1911590Srgrimes			}
1921590Srgrimes			break;
1931590Srgrimes		}
1941590Srgrimes	exit (eval);
1951590Srgrimes}
1961590Srgrimes
1971590Srgrimesvoid
1981590Srgrimescompress(in, out, bits)
19987214Smarkm	const char *in, *out;
2001590Srgrimes	int bits;
2011590Srgrimes{
20287214Smarkm	size_t nr;
2031590Srgrimes	struct stat isb, sb;
2041590Srgrimes	FILE *ifp, *ofp;
2051590Srgrimes	int exists, isreg, oreg;
2061590Srgrimes	u_char buf[1024];
2071590Srgrimes
2081590Srgrimes	exists = !stat(out, &sb);
2091590Srgrimes	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
2101590Srgrimes		return;
2111590Srgrimes	isreg = oreg = !exists || S_ISREG(sb.st_mode);
2121590Srgrimes
2131590Srgrimes	ifp = ofp = NULL;
2141590Srgrimes	if ((ifp = fopen(in, "r")) == NULL) {
2151590Srgrimes		cwarn("%s", in);
2161590Srgrimes		return;
2171590Srgrimes	}
2181590Srgrimes	if (stat(in, &isb)) {		/* DON'T FSTAT! */
2191590Srgrimes		cwarn("%s", in);
2201590Srgrimes		goto err;
2211590Srgrimes	}
2221590Srgrimes	if (!S_ISREG(isb.st_mode))
2231590Srgrimes		isreg = 0;
2241590Srgrimes
2251590Srgrimes	if ((ofp = zopen(out, "w", bits)) == NULL) {
2261590Srgrimes		cwarn("%s", out);
2271590Srgrimes		goto err;
2281590Srgrimes	}
2291590Srgrimes	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
2301590Srgrimes		if (fwrite(buf, 1, nr, ofp) != nr) {
2311590Srgrimes			cwarn("%s", out);
2321590Srgrimes			goto err;
2331590Srgrimes		}
2341590Srgrimes
2351590Srgrimes	if (ferror(ifp) || fclose(ifp)) {
2361590Srgrimes		cwarn("%s", in);
2371590Srgrimes		goto err;
2381590Srgrimes	}
2391590Srgrimes	ifp = NULL;
2401590Srgrimes
2411590Srgrimes	if (fclose(ofp)) {
2421590Srgrimes		cwarn("%s", out);
2431590Srgrimes		goto err;
2441590Srgrimes	}
2451590Srgrimes	ofp = NULL;
2461590Srgrimes
2471590Srgrimes	if (isreg) {
2481590Srgrimes		if (stat(out, &sb)) {
2491590Srgrimes			cwarn("%s", out);
2501590Srgrimes			goto err;
2511590Srgrimes		}
2521590Srgrimes
2531590Srgrimes		if (!force && sb.st_size >= isb.st_size) {
2541590Srgrimes			if (verbose)
2551590Srgrimes		(void)printf("%s: file would grow; left unmodified\n", in);
2561590Srgrimes			if (unlink(out))
2571590Srgrimes				cwarn("%s", out);
2581590Srgrimes			goto err;
2591590Srgrimes		}
2601590Srgrimes
2611590Srgrimes		setfile(out, &isb);
2621590Srgrimes
2631590Srgrimes		if (unlink(in))
2641590Srgrimes			cwarn("%s", in);
2651590Srgrimes
2661590Srgrimes		if (verbose) {
2671590Srgrimes			(void)printf("%s: ", out);
2681590Srgrimes			if (isb.st_size > sb.st_size)
2691590Srgrimes				(void)printf("%.0f%% compression\n",
2701590Srgrimes				    ((float)sb.st_size / isb.st_size) * 100.0);
2711590Srgrimes			else
2721590Srgrimes				(void)printf("%.0f%% expansion\n",
2731590Srgrimes				    ((float)isb.st_size / sb.st_size) * 100.0);
2741590Srgrimes		}
2751590Srgrimes	}
2761590Srgrimes	return;
2771590Srgrimes
2781590Srgrimeserr:	if (ofp) {
2791590Srgrimes		if (oreg)
2801590Srgrimes			(void)unlink(out);
2811590Srgrimes		(void)fclose(ofp);
2821590Srgrimes	}
2831590Srgrimes	if (ifp)
2841590Srgrimes		(void)fclose(ifp);
2851590Srgrimes}
2861590Srgrimes
2871590Srgrimesvoid
2881590Srgrimesdecompress(in, out, bits)
28987214Smarkm	const char *in, *out;
2901590Srgrimes	int bits;
2911590Srgrimes{
29287214Smarkm	size_t nr;
2931590Srgrimes	struct stat sb;
2941590Srgrimes	FILE *ifp, *ofp;
2951590Srgrimes	int exists, isreg, oreg;
2961590Srgrimes	u_char buf[1024];
2971590Srgrimes
2981590Srgrimes	exists = !stat(out, &sb);
2991590Srgrimes	if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
3001590Srgrimes		return;
3011590Srgrimes	isreg = oreg = !exists || S_ISREG(sb.st_mode);
3021590Srgrimes
3031590Srgrimes	ifp = ofp = NULL;
3041590Srgrimes	if ((ofp = fopen(out, "w")) == NULL) {
3051590Srgrimes		cwarn("%s", out);
3061590Srgrimes		return;
3071590Srgrimes	}
3081590Srgrimes
3091590Srgrimes	if ((ifp = zopen(in, "r", bits)) == NULL) {
3101590Srgrimes		cwarn("%s", in);
3111590Srgrimes		goto err;
3121590Srgrimes	}
3131590Srgrimes	if (stat(in, &sb)) {
3141590Srgrimes		cwarn("%s", in);
3151590Srgrimes		goto err;
3161590Srgrimes	}
3171590Srgrimes	if (!S_ISREG(sb.st_mode))
3181590Srgrimes		isreg = 0;
3191590Srgrimes
3201590Srgrimes	while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
3211590Srgrimes		if (fwrite(buf, 1, nr, ofp) != nr) {
3221590Srgrimes			cwarn("%s", out);
3231590Srgrimes			goto err;
3241590Srgrimes		}
3251590Srgrimes
3261590Srgrimes	if (ferror(ifp) || fclose(ifp)) {
3271590Srgrimes		cwarn("%s", in);
3281590Srgrimes		goto err;
3291590Srgrimes	}
3301590Srgrimes	ifp = NULL;
3311590Srgrimes
3321590Srgrimes	if (fclose(ofp)) {
3331590Srgrimes		cwarn("%s", out);
3341590Srgrimes		goto err;
3351590Srgrimes	}
3361590Srgrimes
3371590Srgrimes	if (isreg) {
3381590Srgrimes		setfile(out, &sb);
3391590Srgrimes
3401590Srgrimes		if (unlink(in))
3411590Srgrimes			cwarn("%s", in);
3421590Srgrimes	}
3431590Srgrimes	return;
3441590Srgrimes
3451590Srgrimeserr:	if (ofp) {
3461590Srgrimes		if (oreg)
3471590Srgrimes			(void)unlink(out);
3481590Srgrimes		(void)fclose(ofp);
3491590Srgrimes	}
3501590Srgrimes	if (ifp)
3511590Srgrimes		(void)fclose(ifp);
3521590Srgrimes}
3531590Srgrimes
3541590Srgrimesvoid
3551590Srgrimessetfile(name, fs)
35687214Smarkm	const char *name;
35787214Smarkm	struct stat *fs;
3581590Srgrimes{
3591590Srgrimes	static struct timeval tv[2];
3601590Srgrimes
3611590Srgrimes	fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
3621590Srgrimes
3631590Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
3641590Srgrimes	TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
3651590Srgrimes	if (utimes(name, tv))
3661590Srgrimes		cwarn("utimes: %s", name);
3671590Srgrimes
3681590Srgrimes	/*
3691590Srgrimes	 * Changing the ownership probably won't succeed, unless we're root
3701590Srgrimes	 * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
3711590Srgrimes	 * the mode; current BSD behavior is to remove all setuid bits on
3721590Srgrimes	 * chown.  If chown fails, lose setuid/setgid bits.
3731590Srgrimes	 */
3741590Srgrimes	if (chown(name, fs->st_uid, fs->st_gid)) {
3751590Srgrimes		if (errno != EPERM)
3761590Srgrimes			cwarn("chown: %s", name);
3771590Srgrimes		fs->st_mode &= ~(S_ISUID|S_ISGID);
3781590Srgrimes	}
37960622Shoek	if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP)
38058630Scharnier		cwarn("chmod: %s", name);
3811590Srgrimes
38260622Shoek	if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP)
3831590Srgrimes		cwarn("chflags: %s", name);
3841590Srgrimes}
3851590Srgrimes
3861590Srgrimesint
3871590Srgrimespermission(fname)
38887214Smarkm	const char *fname;
3891590Srgrimes{
3901590Srgrimes	int ch, first;
3911590Srgrimes
3921590Srgrimes	if (!isatty(fileno(stderr)))
3931590Srgrimes		return (0);
3941590Srgrimes	(void)fprintf(stderr, "overwrite %s? ", fname);
3951590Srgrimes	first = ch = getchar();
3961590Srgrimes	while (ch != '\n' && ch != EOF)
3971590Srgrimes		ch = getchar();
3981590Srgrimes	return (first == 'y');
3991590Srgrimes}
4001590Srgrimes
4011590Srgrimesvoid
4021590Srgrimesusage(iscompress)
4031590Srgrimes	int iscompress;
4041590Srgrimes{
4051590Srgrimes	if (iscompress)
4061590Srgrimes		(void)fprintf(stderr,
4071590Srgrimes		    "usage: compress [-cfv] [-b bits] [file ...]\n");
4081590Srgrimes	else
4091590Srgrimes		(void)fprintf(stderr,
4101590Srgrimes		    "usage: uncompress [-c] [-b bits] [file ...]\n");
4111590Srgrimes	exit(1);
4121590Srgrimes}
4131590Srgrimes
4141590Srgrimesvoid
4151590Srgrimes#if __STDC__
4161590Srgrimescwarnx(const char *fmt, ...)
4171590Srgrimes#else
4181590Srgrimescwarnx(fmt, va_alist)
4191590Srgrimes	int eval;
4201590Srgrimes	const char *fmt;
4211590Srgrimes	va_dcl
4221590Srgrimes#endif
4231590Srgrimes{
4241590Srgrimes	va_list ap;
4251590Srgrimes#if __STDC__
4261590Srgrimes	va_start(ap, fmt);
4271590Srgrimes#else
4281590Srgrimes	va_start(ap);
4291590Srgrimes#endif
4301590Srgrimes	vwarnx(fmt, ap);
4311590Srgrimes	va_end(ap);
4321590Srgrimes	eval = 1;
4331590Srgrimes}
4341590Srgrimes
4351590Srgrimesvoid
4361590Srgrimes#if __STDC__
4371590Srgrimescwarn(const char *fmt, ...)
4381590Srgrimes#else
4391590Srgrimescwarn(fmt, va_alist)
4401590Srgrimes	int eval;
4411590Srgrimes	const char *fmt;
4421590Srgrimes	va_dcl
4431590Srgrimes#endif
4441590Srgrimes{
4451590Srgrimes	va_list ap;
4461590Srgrimes#if __STDC__
4471590Srgrimes	va_start(ap, fmt);
4481590Srgrimes#else
4491590Srgrimes	va_start(ap);
4501590Srgrimes#endif
4511590Srgrimes	vwarn(fmt, ap);
4521590Srgrimes	va_end(ap);
4531590Srgrimes	eval = 1;
4541590Srgrimes}
455