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 * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3141568Sarchiestatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1992, 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3487247Smarkm#endif 351590Srgrimes 3687628Sdwmalone#if 0 371590Srgrimes#ifndef lint 3887628Sdwmalonestatic char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; 3958630Scharnier#endif 4087628Sdwmalone#endif 411590Srgrimes 4287628Sdwmalone#include <sys/cdefs.h> 4387628Sdwmalone__FBSDID("$FreeBSD: releng/10.2/usr.bin/compress/compress.c 229403 2012-01-03 18:51:58Z ed $"); 4487628Sdwmalone 451590Srgrimes#include <sys/param.h> 461590Srgrimes#include <sys/stat.h> 4766907Swollman#include <sys/time.h> 481590Srgrimes 491590Srgrimes#include <err.h> 501590Srgrimes#include <errno.h> 5193055Simp#include <stdarg.h> 521590Srgrimes#include <stdio.h> 531590Srgrimes#include <stdlib.h> 541590Srgrimes#include <string.h> 551590Srgrimes#include <unistd.h> 561590Srgrimes 5718053Sbde#include "zopen.h" 5818053Sbde 59227236Sedstatic void compress(const char *, const char *, int); 60227236Sedstatic void cwarn(const char *, ...) __printflike(1, 2); 61227236Sedstatic void cwarnx(const char *, ...) __printflike(1, 2); 62227236Sedstatic void decompress(const char *, const char *, int); 63227236Sedstatic int permission(const char *); 64227236Sedstatic void setfile(const char *, struct stat *); 65227236Sedstatic void usage(int); 661590Srgrimes 67227236Sedstatic int eval, force, verbose; 681590Srgrimes 691590Srgrimesint 70100820Sdwmalonemain(int argc, char *argv[]) 711590Srgrimes{ 7240547Sbde enum {COMPRESS, DECOMPRESS} style; 731590Srgrimes size_t len; 741590Srgrimes int bits, cat, ch; 751590Srgrimes char *p, newname[MAXPATHLEN]; 761590Srgrimes 7740547Sbde cat = 0; 78229403Sed if ((p = strrchr(argv[0], '/')) == NULL) 791590Srgrimes p = argv[0]; 801590Srgrimes else 811590Srgrimes ++p; 821590Srgrimes if (!strcmp(p, "uncompress")) 831590Srgrimes style = DECOMPRESS; 848874Srgrimes else if (!strcmp(p, "compress")) 851590Srgrimes style = COMPRESS; 8640534Smsmith else if (!strcmp(p, "zcat")) { 8740547Sbde cat = 1; 8840534Smsmith style = DECOMPRESS; 8940534Smsmith } else 901590Srgrimes errx(1, "unknown program name"); 911590Srgrimes 9240547Sbde bits = 0; 9324360Simp while ((ch = getopt(argc, argv, "b:cdfv")) != -1) 941590Srgrimes switch(ch) { 951590Srgrimes case 'b': 961590Srgrimes bits = strtol(optarg, &p, 10); 971590Srgrimes if (*p) 981590Srgrimes errx(1, "illegal bit count -- %s", optarg); 991590Srgrimes break; 1001590Srgrimes case 'c': 1011590Srgrimes cat = 1; 1021590Srgrimes break; 1031590Srgrimes case 'd': /* Backward compatible. */ 1041590Srgrimes style = DECOMPRESS; 1051590Srgrimes break; 1061590Srgrimes case 'f': 1071590Srgrimes force = 1; 1081590Srgrimes break; 1091590Srgrimes case 'v': 1101590Srgrimes verbose = 1; 1111590Srgrimes break; 1121590Srgrimes case '?': 1131590Srgrimes default: 1141590Srgrimes usage(style == COMPRESS); 1151590Srgrimes } 1161590Srgrimes argc -= optind; 1171590Srgrimes argv += optind; 1181590Srgrimes 1191590Srgrimes if (argc == 0) { 1201590Srgrimes switch(style) { 1211590Srgrimes case COMPRESS: 1221590Srgrimes (void)compress("/dev/stdin", "/dev/stdout", bits); 1231590Srgrimes break; 1241590Srgrimes case DECOMPRESS: 1251590Srgrimes (void)decompress("/dev/stdin", "/dev/stdout", bits); 1261590Srgrimes break; 1271590Srgrimes } 1281590Srgrimes exit (eval); 1291590Srgrimes } 1301590Srgrimes 1311590Srgrimes if (cat == 1 && argc > 1) 1321590Srgrimes errx(1, "the -c option permits only a single file argument"); 1331590Srgrimes 1341590Srgrimes for (; *argv; ++argv) 1351590Srgrimes switch(style) { 1361590Srgrimes case COMPRESS: 13796772Stjr if (strcmp(*argv, "-") == 0) { 13896772Stjr compress("/dev/stdin", "/dev/stdout", bits); 13996772Stjr break; 14096772Stjr } else if (cat) { 1411590Srgrimes compress(*argv, "/dev/stdout", bits); 1421590Srgrimes break; 1431590Srgrimes } 144229403Sed if ((p = strrchr(*argv, '.')) != NULL && 1451590Srgrimes !strcmp(p, ".Z")) { 1461590Srgrimes cwarnx("%s: name already has trailing .Z", 1471590Srgrimes *argv); 1481590Srgrimes break; 1491590Srgrimes } 1501590Srgrimes len = strlen(*argv); 1511590Srgrimes if (len > sizeof(newname) - 3) { 1521590Srgrimes cwarnx("%s: name too long", *argv); 1531590Srgrimes break; 1541590Srgrimes } 1551590Srgrimes memmove(newname, *argv, len); 1561590Srgrimes newname[len] = '.'; 1571590Srgrimes newname[len + 1] = 'Z'; 1581590Srgrimes newname[len + 2] = '\0'; 1591590Srgrimes compress(*argv, newname, bits); 1601590Srgrimes break; 1611590Srgrimes case DECOMPRESS: 16296772Stjr if (strcmp(*argv, "-") == 0) { 16396772Stjr decompress("/dev/stdin", "/dev/stdout", bits); 16496772Stjr break; 16596772Stjr } 1661590Srgrimes len = strlen(*argv); 167229403Sed if ((p = strrchr(*argv, '.')) == NULL || 1681590Srgrimes strcmp(p, ".Z")) { 1691590Srgrimes if (len > sizeof(newname) - 3) { 1701590Srgrimes cwarnx("%s: name too long", *argv); 1711590Srgrimes break; 1721590Srgrimes } 1731590Srgrimes memmove(newname, *argv, len); 1741590Srgrimes newname[len] = '.'; 1751590Srgrimes newname[len + 1] = 'Z'; 1761590Srgrimes newname[len + 2] = '\0'; 1771590Srgrimes decompress(newname, 1781590Srgrimes cat ? "/dev/stdout" : *argv, bits); 1791590Srgrimes } else { 1801590Srgrimes if (len - 2 > sizeof(newname) - 1) { 1811590Srgrimes cwarnx("%s: name too long", *argv); 1821590Srgrimes break; 1831590Srgrimes } 1841590Srgrimes memmove(newname, *argv, len - 2); 1851590Srgrimes newname[len - 2] = '\0'; 1861590Srgrimes decompress(*argv, 1871590Srgrimes cat ? "/dev/stdout" : newname, bits); 1881590Srgrimes } 1891590Srgrimes break; 1901590Srgrimes } 1911590Srgrimes exit (eval); 1921590Srgrimes} 1931590Srgrimes 194227236Sedstatic void 195100820Sdwmalonecompress(const char *in, const char *out, int bits) 1961590Srgrimes{ 19787214Smarkm size_t nr; 1981590Srgrimes struct stat isb, sb; 1991590Srgrimes FILE *ifp, *ofp; 2001590Srgrimes int exists, isreg, oreg; 2011590Srgrimes u_char buf[1024]; 2021590Srgrimes 2031590Srgrimes exists = !stat(out, &sb); 2041590Srgrimes if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 2051590Srgrimes return; 2061590Srgrimes isreg = oreg = !exists || S_ISREG(sb.st_mode); 2071590Srgrimes 2081590Srgrimes ifp = ofp = NULL; 2091590Srgrimes if ((ifp = fopen(in, "r")) == NULL) { 2101590Srgrimes cwarn("%s", in); 2111590Srgrimes return; 2121590Srgrimes } 2131590Srgrimes if (stat(in, &isb)) { /* DON'T FSTAT! */ 2141590Srgrimes cwarn("%s", in); 2151590Srgrimes goto err; 2161590Srgrimes } 2171590Srgrimes if (!S_ISREG(isb.st_mode)) 2181590Srgrimes isreg = 0; 2191590Srgrimes 2201590Srgrimes if ((ofp = zopen(out, "w", bits)) == NULL) { 2211590Srgrimes cwarn("%s", out); 2221590Srgrimes goto err; 2231590Srgrimes } 2241590Srgrimes while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 2251590Srgrimes if (fwrite(buf, 1, nr, ofp) != nr) { 2261590Srgrimes cwarn("%s", out); 2271590Srgrimes goto err; 2281590Srgrimes } 2291590Srgrimes 2301590Srgrimes if (ferror(ifp) || fclose(ifp)) { 2311590Srgrimes cwarn("%s", in); 2321590Srgrimes goto err; 2331590Srgrimes } 2341590Srgrimes ifp = NULL; 2351590Srgrimes 2361590Srgrimes if (fclose(ofp)) { 2371590Srgrimes cwarn("%s", out); 2381590Srgrimes goto err; 2391590Srgrimes } 2401590Srgrimes ofp = NULL; 2411590Srgrimes 2421590Srgrimes if (isreg) { 2431590Srgrimes if (stat(out, &sb)) { 2441590Srgrimes cwarn("%s", out); 2451590Srgrimes goto err; 2461590Srgrimes } 2471590Srgrimes 2481590Srgrimes if (!force && sb.st_size >= isb.st_size) { 2491590Srgrimes if (verbose) 25096770Stjr (void)fprintf(stderr, "%s: file would grow; left unmodified\n", 25196770Stjr in); 25296769Stjr eval = 2; 2531590Srgrimes if (unlink(out)) 2541590Srgrimes cwarn("%s", out); 2551590Srgrimes goto err; 2561590Srgrimes } 2571590Srgrimes 2581590Srgrimes setfile(out, &isb); 2591590Srgrimes 2601590Srgrimes if (unlink(in)) 2611590Srgrimes cwarn("%s", in); 2621590Srgrimes 2631590Srgrimes if (verbose) { 26496770Stjr (void)fprintf(stderr, "%s: ", out); 2651590Srgrimes if (isb.st_size > sb.st_size) 26696770Stjr (void)fprintf(stderr, "%.0f%% compression\n", 2671590Srgrimes ((float)sb.st_size / isb.st_size) * 100.0); 2681590Srgrimes else 26996770Stjr (void)fprintf(stderr, "%.0f%% expansion\n", 2701590Srgrimes ((float)isb.st_size / sb.st_size) * 100.0); 2711590Srgrimes } 2721590Srgrimes } 2731590Srgrimes return; 2741590Srgrimes 2751590Srgrimeserr: if (ofp) { 2761590Srgrimes if (oreg) 2771590Srgrimes (void)unlink(out); 2781590Srgrimes (void)fclose(ofp); 2791590Srgrimes } 2801590Srgrimes if (ifp) 2811590Srgrimes (void)fclose(ifp); 2821590Srgrimes} 2831590Srgrimes 284227236Sedstatic void 285100820Sdwmalonedecompress(const char *in, const char *out, int bits) 2861590Srgrimes{ 28787214Smarkm size_t nr; 2881590Srgrimes struct stat sb; 2891590Srgrimes FILE *ifp, *ofp; 2901590Srgrimes int exists, isreg, oreg; 2911590Srgrimes u_char buf[1024]; 2921590Srgrimes 2931590Srgrimes exists = !stat(out, &sb); 2941590Srgrimes if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 2951590Srgrimes return; 2961590Srgrimes isreg = oreg = !exists || S_ISREG(sb.st_mode); 2971590Srgrimes 2981590Srgrimes ifp = ofp = NULL; 2991590Srgrimes if ((ifp = zopen(in, "r", bits)) == NULL) { 3001590Srgrimes cwarn("%s", in); 301116336Strhodes return; 3021590Srgrimes } 3031590Srgrimes if (stat(in, &sb)) { 3041590Srgrimes cwarn("%s", in); 3051590Srgrimes goto err; 3061590Srgrimes } 3071590Srgrimes if (!S_ISREG(sb.st_mode)) 3081590Srgrimes isreg = 0; 3091590Srgrimes 310116336Strhodes /* 311116336Strhodes * Try to read the first few uncompressed bytes from the input file 312116336Strhodes * before blindly truncating the output file. 313116336Strhodes */ 314116336Strhodes if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) { 315116336Strhodes cwarn("%s", in); 316116336Strhodes (void)fclose(ifp); 317116336Strhodes return; 318116336Strhodes } 319116336Strhodes if ((ofp = fopen(out, "w")) == NULL || 320116336Strhodes (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) { 321116336Strhodes cwarn("%s", out); 322116336Strhodes (void)fclose(ifp); 323116336Strhodes return; 324116336Strhodes } 325116336Strhodes 3261590Srgrimes while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 3271590Srgrimes if (fwrite(buf, 1, nr, ofp) != nr) { 3281590Srgrimes cwarn("%s", out); 3291590Srgrimes goto err; 3301590Srgrimes } 3311590Srgrimes 3321590Srgrimes if (ferror(ifp) || fclose(ifp)) { 3331590Srgrimes cwarn("%s", in); 3341590Srgrimes goto err; 3351590Srgrimes } 3361590Srgrimes ifp = NULL; 3371590Srgrimes 3381590Srgrimes if (fclose(ofp)) { 3391590Srgrimes cwarn("%s", out); 3401590Srgrimes goto err; 3411590Srgrimes } 3421590Srgrimes 3431590Srgrimes if (isreg) { 3441590Srgrimes setfile(out, &sb); 3451590Srgrimes 3461590Srgrimes if (unlink(in)) 3471590Srgrimes cwarn("%s", in); 3481590Srgrimes } 3491590Srgrimes return; 3501590Srgrimes 3511590Srgrimeserr: if (ofp) { 3521590Srgrimes if (oreg) 3531590Srgrimes (void)unlink(out); 3541590Srgrimes (void)fclose(ofp); 3551590Srgrimes } 3561590Srgrimes if (ifp) 3571590Srgrimes (void)fclose(ifp); 3581590Srgrimes} 3591590Srgrimes 360227236Sedstatic void 361100820Sdwmalonesetfile(const char *name, struct stat *fs) 3621590Srgrimes{ 3631590Srgrimes static struct timeval tv[2]; 3641590Srgrimes 3651590Srgrimes fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 3661590Srgrimes 367205793Sed TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim); 368205793Sed TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim); 3691590Srgrimes if (utimes(name, tv)) 3701590Srgrimes cwarn("utimes: %s", name); 3711590Srgrimes 3721590Srgrimes /* 3731590Srgrimes * Changing the ownership probably won't succeed, unless we're root 3741590Srgrimes * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 3751590Srgrimes * the mode; current BSD behavior is to remove all setuid bits on 3761590Srgrimes * chown. If chown fails, lose setuid/setgid bits. 3771590Srgrimes */ 3781590Srgrimes if (chown(name, fs->st_uid, fs->st_gid)) { 3791590Srgrimes if (errno != EPERM) 3801590Srgrimes cwarn("chown: %s", name); 3811590Srgrimes fs->st_mode &= ~(S_ISUID|S_ISGID); 3821590Srgrimes } 38360622Shoek if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP) 38458630Scharnier cwarn("chmod: %s", name); 3851590Srgrimes 38660622Shoek if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP) 3871590Srgrimes cwarn("chflags: %s", name); 3881590Srgrimes} 3891590Srgrimes 390227236Sedstatic int 391100820Sdwmalonepermission(const char *fname) 3921590Srgrimes{ 3931590Srgrimes int ch, first; 3941590Srgrimes 3951590Srgrimes if (!isatty(fileno(stderr))) 3961590Srgrimes return (0); 3971590Srgrimes (void)fprintf(stderr, "overwrite %s? ", fname); 3981590Srgrimes first = ch = getchar(); 3991590Srgrimes while (ch != '\n' && ch != EOF) 4001590Srgrimes ch = getchar(); 4011590Srgrimes return (first == 'y'); 4021590Srgrimes} 4031590Srgrimes 404227236Sedstatic void 405100820Sdwmaloneusage(int iscompress) 4061590Srgrimes{ 4071590Srgrimes if (iscompress) 4081590Srgrimes (void)fprintf(stderr, 4091590Srgrimes "usage: compress [-cfv] [-b bits] [file ...]\n"); 4101590Srgrimes else 4111590Srgrimes (void)fprintf(stderr, 4121590Srgrimes "usage: uncompress [-c] [-b bits] [file ...]\n"); 4131590Srgrimes exit(1); 4141590Srgrimes} 4151590Srgrimes 416227236Sedstatic void 4171590Srgrimescwarnx(const char *fmt, ...) 4181590Srgrimes{ 4191590Srgrimes va_list ap; 42093055Simp 4211590Srgrimes va_start(ap, fmt); 4221590Srgrimes vwarnx(fmt, ap); 4231590Srgrimes va_end(ap); 4241590Srgrimes eval = 1; 4251590Srgrimes} 4261590Srgrimes 427227236Sedstatic void 4281590Srgrimescwarn(const char *fmt, ...) 4291590Srgrimes{ 4301590Srgrimes va_list ap; 43193055Simp 4321590Srgrimes va_start(ap, fmt); 4331590Srgrimes vwarn(fmt, ap); 4341590Srgrimes va_end(ap); 4351590Srgrimes eval = 1; 4361590Srgrimes} 437