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