compress.c revision 116336
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 116336 2003-06-14 13:41:31Z trhodes $"); 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 74100820Sdwmalonemain(int argc, char *argv[]) 751590Srgrimes{ 7640547Sbde enum {COMPRESS, DECOMPRESS} style; 771590Srgrimes size_t len; 781590Srgrimes int bits, cat, ch; 791590Srgrimes char *p, newname[MAXPATHLEN]; 801590Srgrimes 8140547Sbde cat = 0; 821590Srgrimes if ((p = rindex(argv[0], '/')) == NULL) 831590Srgrimes p = argv[0]; 841590Srgrimes else 851590Srgrimes ++p; 861590Srgrimes if (!strcmp(p, "uncompress")) 871590Srgrimes style = DECOMPRESS; 888874Srgrimes else if (!strcmp(p, "compress")) 891590Srgrimes style = COMPRESS; 9040534Smsmith else if (!strcmp(p, "zcat")) { 9140547Sbde cat = 1; 9240534Smsmith style = DECOMPRESS; 9340534Smsmith } else 941590Srgrimes errx(1, "unknown program name"); 951590Srgrimes 9640547Sbde bits = 0; 9724360Simp while ((ch = getopt(argc, argv, "b:cdfv")) != -1) 981590Srgrimes switch(ch) { 991590Srgrimes case 'b': 1001590Srgrimes bits = strtol(optarg, &p, 10); 1011590Srgrimes if (*p) 1021590Srgrimes errx(1, "illegal bit count -- %s", optarg); 1031590Srgrimes break; 1041590Srgrimes case 'c': 1051590Srgrimes cat = 1; 1061590Srgrimes break; 1071590Srgrimes case 'd': /* Backward compatible. */ 1081590Srgrimes style = DECOMPRESS; 1091590Srgrimes break; 1101590Srgrimes case 'f': 1111590Srgrimes force = 1; 1121590Srgrimes break; 1131590Srgrimes case 'v': 1141590Srgrimes verbose = 1; 1151590Srgrimes break; 1161590Srgrimes case '?': 1171590Srgrimes default: 1181590Srgrimes usage(style == COMPRESS); 1191590Srgrimes } 1201590Srgrimes argc -= optind; 1211590Srgrimes argv += optind; 1221590Srgrimes 1231590Srgrimes if (argc == 0) { 1241590Srgrimes switch(style) { 1251590Srgrimes case COMPRESS: 1261590Srgrimes (void)compress("/dev/stdin", "/dev/stdout", bits); 1271590Srgrimes break; 1281590Srgrimes case DECOMPRESS: 1291590Srgrimes (void)decompress("/dev/stdin", "/dev/stdout", bits); 1301590Srgrimes break; 1311590Srgrimes } 1321590Srgrimes exit (eval); 1331590Srgrimes } 1341590Srgrimes 1351590Srgrimes if (cat == 1 && argc > 1) 1361590Srgrimes errx(1, "the -c option permits only a single file argument"); 1371590Srgrimes 1381590Srgrimes for (; *argv; ++argv) 1391590Srgrimes switch(style) { 1401590Srgrimes case COMPRESS: 14196772Stjr if (strcmp(*argv, "-") == 0) { 14296772Stjr compress("/dev/stdin", "/dev/stdout", bits); 14396772Stjr break; 14496772Stjr } else if (cat) { 1451590Srgrimes compress(*argv, "/dev/stdout", bits); 1461590Srgrimes break; 1471590Srgrimes } 1481590Srgrimes if ((p = rindex(*argv, '.')) != NULL && 1491590Srgrimes !strcmp(p, ".Z")) { 1501590Srgrimes cwarnx("%s: name already has trailing .Z", 1511590Srgrimes *argv); 1521590Srgrimes break; 1531590Srgrimes } 1541590Srgrimes len = strlen(*argv); 1551590Srgrimes if (len > sizeof(newname) - 3) { 1561590Srgrimes cwarnx("%s: name too long", *argv); 1571590Srgrimes break; 1581590Srgrimes } 1591590Srgrimes memmove(newname, *argv, len); 1601590Srgrimes newname[len] = '.'; 1611590Srgrimes newname[len + 1] = 'Z'; 1621590Srgrimes newname[len + 2] = '\0'; 1631590Srgrimes compress(*argv, newname, bits); 1641590Srgrimes break; 1651590Srgrimes case DECOMPRESS: 16696772Stjr if (strcmp(*argv, "-") == 0) { 16796772Stjr decompress("/dev/stdin", "/dev/stdout", bits); 16896772Stjr break; 16996772Stjr } 1701590Srgrimes len = strlen(*argv); 1711590Srgrimes if ((p = rindex(*argv, '.')) == NULL || 1721590Srgrimes strcmp(p, ".Z")) { 1731590Srgrimes if (len > sizeof(newname) - 3) { 1741590Srgrimes cwarnx("%s: name too long", *argv); 1751590Srgrimes break; 1761590Srgrimes } 1771590Srgrimes memmove(newname, *argv, len); 1781590Srgrimes newname[len] = '.'; 1791590Srgrimes newname[len + 1] = 'Z'; 1801590Srgrimes newname[len + 2] = '\0'; 1811590Srgrimes decompress(newname, 1821590Srgrimes cat ? "/dev/stdout" : *argv, bits); 1831590Srgrimes } else { 1841590Srgrimes if (len - 2 > sizeof(newname) - 1) { 1851590Srgrimes cwarnx("%s: name too long", *argv); 1861590Srgrimes break; 1871590Srgrimes } 1881590Srgrimes memmove(newname, *argv, len - 2); 1891590Srgrimes newname[len - 2] = '\0'; 1901590Srgrimes decompress(*argv, 1911590Srgrimes cat ? "/dev/stdout" : newname, bits); 1921590Srgrimes } 1931590Srgrimes break; 1941590Srgrimes } 1951590Srgrimes exit (eval); 1961590Srgrimes} 1971590Srgrimes 1981590Srgrimesvoid 199100820Sdwmalonecompress(const char *in, const char *out, int bits) 2001590Srgrimes{ 20187214Smarkm size_t nr; 2021590Srgrimes struct stat isb, sb; 2031590Srgrimes FILE *ifp, *ofp; 2041590Srgrimes int exists, isreg, oreg; 2051590Srgrimes u_char buf[1024]; 2061590Srgrimes 2071590Srgrimes exists = !stat(out, &sb); 2081590Srgrimes if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 2091590Srgrimes return; 2101590Srgrimes isreg = oreg = !exists || S_ISREG(sb.st_mode); 2111590Srgrimes 2121590Srgrimes ifp = ofp = NULL; 2131590Srgrimes if ((ifp = fopen(in, "r")) == NULL) { 2141590Srgrimes cwarn("%s", in); 2151590Srgrimes return; 2161590Srgrimes } 2171590Srgrimes if (stat(in, &isb)) { /* DON'T FSTAT! */ 2181590Srgrimes cwarn("%s", in); 2191590Srgrimes goto err; 2201590Srgrimes } 2211590Srgrimes if (!S_ISREG(isb.st_mode)) 2221590Srgrimes isreg = 0; 2231590Srgrimes 2241590Srgrimes if ((ofp = zopen(out, "w", bits)) == NULL) { 2251590Srgrimes cwarn("%s", out); 2261590Srgrimes goto err; 2271590Srgrimes } 2281590Srgrimes while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 2291590Srgrimes if (fwrite(buf, 1, nr, ofp) != nr) { 2301590Srgrimes cwarn("%s", out); 2311590Srgrimes goto err; 2321590Srgrimes } 2331590Srgrimes 2341590Srgrimes if (ferror(ifp) || fclose(ifp)) { 2351590Srgrimes cwarn("%s", in); 2361590Srgrimes goto err; 2371590Srgrimes } 2381590Srgrimes ifp = NULL; 2391590Srgrimes 2401590Srgrimes if (fclose(ofp)) { 2411590Srgrimes cwarn("%s", out); 2421590Srgrimes goto err; 2431590Srgrimes } 2441590Srgrimes ofp = NULL; 2451590Srgrimes 2461590Srgrimes if (isreg) { 2471590Srgrimes if (stat(out, &sb)) { 2481590Srgrimes cwarn("%s", out); 2491590Srgrimes goto err; 2501590Srgrimes } 2511590Srgrimes 2521590Srgrimes if (!force && sb.st_size >= isb.st_size) { 2531590Srgrimes if (verbose) 25496770Stjr (void)fprintf(stderr, "%s: file would grow; left unmodified\n", 25596770Stjr in); 25696769Stjr eval = 2; 2571590Srgrimes if (unlink(out)) 2581590Srgrimes cwarn("%s", out); 2591590Srgrimes goto err; 2601590Srgrimes } 2611590Srgrimes 2621590Srgrimes setfile(out, &isb); 2631590Srgrimes 2641590Srgrimes if (unlink(in)) 2651590Srgrimes cwarn("%s", in); 2661590Srgrimes 2671590Srgrimes if (verbose) { 26896770Stjr (void)fprintf(stderr, "%s: ", out); 2691590Srgrimes if (isb.st_size > sb.st_size) 27096770Stjr (void)fprintf(stderr, "%.0f%% compression\n", 2711590Srgrimes ((float)sb.st_size / isb.st_size) * 100.0); 2721590Srgrimes else 27396770Stjr (void)fprintf(stderr, "%.0f%% expansion\n", 2741590Srgrimes ((float)isb.st_size / sb.st_size) * 100.0); 2751590Srgrimes } 2761590Srgrimes } 2771590Srgrimes return; 2781590Srgrimes 2791590Srgrimeserr: if (ofp) { 2801590Srgrimes if (oreg) 2811590Srgrimes (void)unlink(out); 2821590Srgrimes (void)fclose(ofp); 2831590Srgrimes } 2841590Srgrimes if (ifp) 2851590Srgrimes (void)fclose(ifp); 2861590Srgrimes} 2871590Srgrimes 2881590Srgrimesvoid 289100820Sdwmalonedecompress(const char *in, const char *out, int bits) 2901590Srgrimes{ 29187214Smarkm size_t nr; 2921590Srgrimes struct stat sb; 2931590Srgrimes FILE *ifp, *ofp; 2941590Srgrimes int exists, isreg, oreg; 2951590Srgrimes u_char buf[1024]; 2961590Srgrimes 2971590Srgrimes exists = !stat(out, &sb); 2981590Srgrimes if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 2991590Srgrimes return; 3001590Srgrimes isreg = oreg = !exists || S_ISREG(sb.st_mode); 3011590Srgrimes 3021590Srgrimes ifp = ofp = NULL; 3031590Srgrimes if ((ifp = zopen(in, "r", bits)) == NULL) { 3041590Srgrimes cwarn("%s", in); 305116336Strhodes return; 3061590Srgrimes } 3071590Srgrimes if (stat(in, &sb)) { 3081590Srgrimes cwarn("%s", in); 3091590Srgrimes goto err; 3101590Srgrimes } 3111590Srgrimes if (!S_ISREG(sb.st_mode)) 3121590Srgrimes isreg = 0; 3131590Srgrimes 314116336Strhodes /* 315116336Strhodes * Try to read the first few uncompressed bytes from the input file 316116336Strhodes * before blindly truncating the output file. 317116336Strhodes */ 318116336Strhodes if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) { 319116336Strhodes cwarn("%s", in); 320116336Strhodes (void)fclose(ifp); 321116336Strhodes return; 322116336Strhodes } 323116336Strhodes if ((ofp = fopen(out, "w")) == NULL || 324116336Strhodes (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) { 325116336Strhodes cwarn("%s", out); 326116336Strhodes (void)fclose(ifp); 327116336Strhodes return; 328116336Strhodes } 329116336Strhodes 3301590Srgrimes while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 3311590Srgrimes if (fwrite(buf, 1, nr, ofp) != nr) { 3321590Srgrimes cwarn("%s", out); 3331590Srgrimes goto err; 3341590Srgrimes } 3351590Srgrimes 3361590Srgrimes if (ferror(ifp) || fclose(ifp)) { 3371590Srgrimes cwarn("%s", in); 3381590Srgrimes goto err; 3391590Srgrimes } 3401590Srgrimes ifp = NULL; 3411590Srgrimes 3421590Srgrimes if (fclose(ofp)) { 3431590Srgrimes cwarn("%s", out); 3441590Srgrimes goto err; 3451590Srgrimes } 3461590Srgrimes 3471590Srgrimes if (isreg) { 3481590Srgrimes setfile(out, &sb); 3491590Srgrimes 3501590Srgrimes if (unlink(in)) 3511590Srgrimes cwarn("%s", in); 3521590Srgrimes } 3531590Srgrimes return; 3541590Srgrimes 3551590Srgrimeserr: if (ofp) { 3561590Srgrimes if (oreg) 3571590Srgrimes (void)unlink(out); 3581590Srgrimes (void)fclose(ofp); 3591590Srgrimes } 3601590Srgrimes if (ifp) 3611590Srgrimes (void)fclose(ifp); 3621590Srgrimes} 3631590Srgrimes 3641590Srgrimesvoid 365100820Sdwmalonesetfile(const char *name, struct stat *fs) 3661590Srgrimes{ 3671590Srgrimes static struct timeval tv[2]; 3681590Srgrimes 3691590Srgrimes fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 3701590Srgrimes 3711590Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 3721590Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 3731590Srgrimes if (utimes(name, tv)) 3741590Srgrimes cwarn("utimes: %s", name); 3751590Srgrimes 3761590Srgrimes /* 3771590Srgrimes * Changing the ownership probably won't succeed, unless we're root 3781590Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 3791590Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 3801590Srgrimes * chown. If chown fails, lose setuid/setgid bits. 3811590Srgrimes */ 3821590Srgrimes if (chown(name, fs->st_uid, fs->st_gid)) { 3831590Srgrimes if (errno != EPERM) 3841590Srgrimes cwarn("chown: %s", name); 3851590Srgrimes fs->st_mode &= ~(S_ISUID|S_ISGID); 3861590Srgrimes } 38760622Shoek if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP) 38858630Scharnier cwarn("chmod: %s", name); 3891590Srgrimes 39060622Shoek if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP) 3911590Srgrimes cwarn("chflags: %s", name); 3921590Srgrimes} 3931590Srgrimes 3941590Srgrimesint 395100820Sdwmalonepermission(const char *fname) 3961590Srgrimes{ 3971590Srgrimes int ch, first; 3981590Srgrimes 3991590Srgrimes if (!isatty(fileno(stderr))) 4001590Srgrimes return (0); 4011590Srgrimes (void)fprintf(stderr, "overwrite %s? ", fname); 4021590Srgrimes first = ch = getchar(); 4031590Srgrimes while (ch != '\n' && ch != EOF) 4041590Srgrimes ch = getchar(); 4051590Srgrimes return (first == 'y'); 4061590Srgrimes} 4071590Srgrimes 4081590Srgrimesvoid 409100820Sdwmaloneusage(int iscompress) 4101590Srgrimes{ 4111590Srgrimes if (iscompress) 4121590Srgrimes (void)fprintf(stderr, 4131590Srgrimes "usage: compress [-cfv] [-b bits] [file ...]\n"); 4141590Srgrimes else 4151590Srgrimes (void)fprintf(stderr, 4161590Srgrimes "usage: uncompress [-c] [-b bits] [file ...]\n"); 4171590Srgrimes exit(1); 4181590Srgrimes} 4191590Srgrimes 4201590Srgrimesvoid 4211590Srgrimescwarnx(const char *fmt, ...) 4221590Srgrimes{ 4231590Srgrimes va_list ap; 42493055Simp 4251590Srgrimes va_start(ap, fmt); 4261590Srgrimes vwarnx(fmt, ap); 4271590Srgrimes va_end(ap); 4281590Srgrimes eval = 1; 4291590Srgrimes} 4301590Srgrimes 4311590Srgrimesvoid 4321590Srgrimescwarn(const char *fmt, ...) 4331590Srgrimes{ 4341590Srgrimes va_list ap; 43593055Simp 4361590Srgrimes va_start(ap, fmt); 4371590Srgrimes vwarn(fmt, ap); 4381590Srgrimes va_end(ap); 4391590Srgrimes eval = 1; 4401590Srgrimes} 441