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