1251875Speter/*- 2251875Speter * SPDX-License-Identifier: BSD-3-Clause 3251875Speter * 4251875Speter * Copyright (c) 1992, 1993 5251875Speter * The Regents of the University of California. All rights reserved. 6251875Speter * 7251875Speter * Redistribution and use in source and binary forms, with or without 8251875Speter * modification, are permitted provided that the following conditions 9251875Speter * are met: 10251875Speter * 1. Redistributions of source code must retain the above copyright 11251875Speter * notice, this list of conditions and the following disclaimer. 12251875Speter * 2. Redistributions in binary form must reproduce the above copyright 13251875Speter * notice, this list of conditions and the following disclaimer in the 14251875Speter * documentation and/or other materials provided with the distribution. 15251875Speter * 3. Neither the name of the University nor the names of its contributors 16251875Speter * may be used to endorse or promote products derived from this software 17251875Speter * without specific prior written permission. 18251875Speter * 19251875Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20251875Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21251875Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22251875Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23251875Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24251875Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25251875Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26251875Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27251875Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28251875Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29251875Speter * SUCH DAMAGE. 30251875Speter */ 31251875Speter 32251875Speter#include <sys/param.h> 33251875Speter#include <sys/stat.h> 34251875Speter#include <sys/time.h> 35251875Speter 36251875Speter#include <err.h> 37251875Speter#include <errno.h> 38251875Speter#include <fcntl.h> 39251875Speter#include <stdarg.h> 40251875Speter#include <stdio.h> 41251875Speter#include <stdlib.h> 42251875Speter#include <string.h> 43251875Speter#include <unistd.h> 44251875Speter 45251875Speter#include "zopen.h" 46251875Speter 47251875Speterstatic void compress(const char *, const char *, int); 48251875Speterstatic void cwarn(const char *, ...) __printflike(1, 2); 49251875Speterstatic void cwarnx(const char *, ...) __printflike(1, 2); 50251875Speterstatic void decompress(const char *, const char *, int); 51251875Speterstatic int permission(const char *); 52251875Speterstatic void setfile(const char *, struct stat *); 53251875Speterstatic void usage(int); 54251875Speter 55251875Speterstatic int eval, force, verbose; 56251875Speter 57251875Speterint 58251875Spetermain(int argc, char *argv[]) 59251875Speter{ 60251875Speter enum {COMPRESS, DECOMPRESS} style; 61251875Speter size_t len; 62251875Speter int bits, cat, ch; 63251875Speter char *p, newname[MAXPATHLEN]; 64251875Speter 65251875Speter cat = 0; 66251875Speter if ((p = strrchr(argv[0], '/')) == NULL) 67251875Speter p = argv[0]; 68251875Speter else 69251875Speter ++p; 70251875Speter if (!strcmp(p, "uncompress")) 71251875Speter style = DECOMPRESS; 72251875Speter else if (!strcmp(p, "compress")) 73251875Speter style = COMPRESS; 74251875Speter else if (!strcmp(p, "zcat")) { 75251875Speter cat = 1; 76251875Speter style = DECOMPRESS; 77251875Speter } else 78251875Speter errx(1, "unknown program name"); 79251875Speter 80251875Speter bits = 0; 81251875Speter while ((ch = getopt(argc, argv, "b:cdfv")) != -1) 82251875Speter switch(ch) { 83251875Speter case 'b': 84251875Speter bits = strtol(optarg, &p, 10); 85251875Speter if (*p) 86251875Speter errx(1, "illegal bit count -- %s", optarg); 87251875Speter break; 88251875Speter case 'c': 89251875Speter cat = 1; 90251875Speter break; 91251875Speter case 'd': /* Backward compatible. */ 92251875Speter style = DECOMPRESS; 93251875Speter break; 94251875Speter case 'f': 95251875Speter force = 1; 96251875Speter break; 97251875Speter case 'v': 98251875Speter verbose = 1; 99251875Speter break; 100251875Speter case '?': 101251875Speter default: 102251875Speter usage(style == COMPRESS); 103251875Speter } 104251875Speter argc -= optind; 105251875Speter argv += optind; 106251875Speter 107251875Speter if (argc == 0) { 108251875Speter switch(style) { 109251875Speter case COMPRESS: 110251875Speter (void)compress("/dev/stdin", "/dev/stdout", bits); 111251875Speter break; 112251875Speter case DECOMPRESS: 113251875Speter (void)decompress("/dev/stdin", "/dev/stdout", bits); 114251875Speter break; 115251875Speter } 116251875Speter exit (eval); 117251875Speter } 118251875Speter 119251875Speter if (cat == 1 && style == COMPRESS && argc > 1) 120251875Speter errx(1, "the -c option permits only a single file argument"); 121251875Speter 122251875Speter for (; *argv; ++argv) 123251875Speter switch(style) { 124251875Speter case COMPRESS: 125251875Speter if (strcmp(*argv, "-") == 0) { 126251875Speter compress("/dev/stdin", "/dev/stdout", bits); 127251875Speter break; 128251875Speter } else if (cat) { 129251875Speter compress(*argv, "/dev/stdout", bits); 130251875Speter break; 131251875Speter } 132251875Speter if ((p = strrchr(*argv, '.')) != NULL && 133251875Speter !strcmp(p, ".Z")) { 134251875Speter cwarnx("%s: name already has trailing .Z", 135251875Speter *argv); 136251875Speter break; 137251875Speter } 138251875Speter len = strlen(*argv); 139251875Speter if (len > sizeof(newname) - 3) { 140251875Speter cwarnx("%s: name too long", *argv); 141251875Speter break; 142251875Speter } 143251875Speter memmove(newname, *argv, len); 144251875Speter newname[len] = '.'; 145251875Speter newname[len + 1] = 'Z'; 146251875Speter newname[len + 2] = '\0'; 147251875Speter compress(*argv, newname, bits); 148251875Speter break; 149251875Speter case DECOMPRESS: 150251875Speter if (strcmp(*argv, "-") == 0) { 151251875Speter decompress("/dev/stdin", "/dev/stdout", bits); 152251875Speter break; 153251875Speter } 154251875Speter len = strlen(*argv); 155251875Speter if ((p = strrchr(*argv, '.')) == NULL || 156251875Speter strcmp(p, ".Z")) { 157251875Speter if (len > sizeof(newname) - 3) { 158251875Speter cwarnx("%s: name too long", *argv); 159251875Speter break; 160251875Speter } 161251875Speter memmove(newname, *argv, len); 162251875Speter newname[len] = '.'; 163251875Speter newname[len + 1] = 'Z'; 164251875Speter newname[len + 2] = '\0'; 165251875Speter decompress(newname, 166251875Speter cat ? "/dev/stdout" : *argv, bits); 167251875Speter } else { 168251875Speter if (len - 2 > sizeof(newname) - 1) { 169251875Speter cwarnx("%s: name too long", *argv); 170251875Speter break; 171251875Speter } 172251875Speter memmove(newname, *argv, len - 2); 173251875Speter newname[len - 2] = '\0'; 174251875Speter decompress(*argv, 175251875Speter cat ? "/dev/stdout" : newname, bits); 176251875Speter } 177251875Speter break; 178251875Speter } 179251875Speter exit (eval); 180251875Speter} 181251875Speter 182251875Speterstatic void 183251875Spetercompress(const char *in, const char *out, int bits) 184251875Speter{ 185251875Speter size_t nr; 186251875Speter struct stat isb, sb; 187251875Speter FILE *ifp, *ofp; 188251875Speter int exists, isreg, oreg; 189251875Speter u_char buf[1024]; 190251875Speter 191251875Speter exists = !stat(out, &sb); 192251875Speter if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 193251875Speter return; 194251875Speter isreg = oreg = !exists || S_ISREG(sb.st_mode); 195251875Speter 196251875Speter ifp = ofp = NULL; 197251875Speter if ((ifp = fopen(in, "r")) == NULL) { 198251875Speter cwarn("%s", in); 199251875Speter return; 200251875Speter } 201251875Speter if (stat(in, &isb)) { /* DON'T FSTAT! */ 202251875Speter cwarn("%s", in); 203251875Speter goto err; 204251875Speter } 205251875Speter if (!S_ISREG(isb.st_mode)) 206251875Speter isreg = 0; 207251875Speter 208251875Speter if ((ofp = zopen(out, "w", bits)) == NULL) { 209251875Speter cwarn("%s", out); 210251875Speter goto err; 211251875Speter } 212251875Speter while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 213251875Speter if (fwrite(buf, 1, nr, ofp) != nr) { 214251875Speter cwarn("%s", out); 215251875Speter goto err; 216251875Speter } 217251875Speter 218251875Speter if (ferror(ifp) || fclose(ifp)) { 219251875Speter cwarn("%s", in); 220251875Speter goto err; 221251875Speter } 222251875Speter ifp = NULL; 223251875Speter 224251875Speter if (fclose(ofp)) { 225251875Speter cwarn("%s", out); 226251875Speter goto err; 227251875Speter } 228251875Speter ofp = NULL; 229251875Speter 230251875Speter if (isreg) { 231251875Speter if (stat(out, &sb)) { 232251875Speter cwarn("%s", out); 233251875Speter goto err; 234251875Speter } 235251875Speter 236251875Speter if (!force && sb.st_size >= isb.st_size) { 237251875Speter if (verbose) 238251875Speter (void)fprintf(stderr, "%s: file would grow; left unmodified\n", 239251875Speter in); 240251875Speter eval = 2; 241251875Speter if (unlink(out)) 242251875Speter cwarn("%s", out); 243251875Speter goto err; 244251875Speter } 245251875Speter 246251875Speter setfile(out, &isb); 247251875Speter 248251875Speter if (unlink(in)) 249251875Speter cwarn("%s", in); 250251875Speter 251251875Speter if (verbose) { 252251875Speter (void)fprintf(stderr, "%s: ", out); 253251875Speter if (isb.st_size > sb.st_size) 254251875Speter (void)fprintf(stderr, "%.0f%% compression\n", 255251875Speter ((float)sb.st_size / isb.st_size) * 100.0); 256251875Speter else 257251875Speter (void)fprintf(stderr, "%.0f%% expansion\n", 258251875Speter ((float)isb.st_size / sb.st_size) * 100.0); 259251875Speter } 260251875Speter } 261251875Speter return; 262251875Speter 263251875Spetererr: if (ofp) { 264251875Speter if (oreg) 265251875Speter (void)unlink(out); 266251875Speter (void)fclose(ofp); 267251875Speter } 268251875Speter if (ifp) 269251875Speter (void)fclose(ifp); 270251875Speter} 271251875Speter 272251875Speterstatic void 273251875Speterdecompress(const char *in, const char *out, int bits) 274251875Speter{ 275251875Speter size_t nr; 276251875Speter struct stat sb; 277251875Speter FILE *ifp, *ofp; 278251875Speter int exists, isreg, oreg; 279251875Speter u_char buf[1024]; 280251875Speter 281251875Speter exists = !stat(out, &sb); 282251875Speter if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 283251875Speter return; 284251875Speter isreg = oreg = !exists || S_ISREG(sb.st_mode); 285251875Speter 286251875Speter ifp = ofp = NULL; 287251875Speter if ((ifp = zopen(in, "r", bits)) == NULL) { 288251875Speter cwarn("%s", in); 289251875Speter return; 290251875Speter } 291251875Speter if (stat(in, &sb)) { 292251875Speter cwarn("%s", in); 293251875Speter goto err; 294251875Speter } 295251875Speter if (!S_ISREG(sb.st_mode)) 296251875Speter isreg = 0; 297251875Speter 298251875Speter /* 299251875Speter * Try to read the first few uncompressed bytes from the input file 300251875Speter * before blindly truncating the output file. 301251875Speter */ 302251875Speter if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) { 303251875Speter cwarn("%s", in); 304251875Speter (void)fclose(ifp); 305251875Speter return; 306251875Speter } 307251875Speter if ((ofp = fopen(out, "w")) == NULL || 308251875Speter (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) { 309251875Speter cwarn("%s", out); 310251875Speter if (ofp) 311251875Speter (void)fclose(ofp); 312251875Speter (void)fclose(ifp); 313251875Speter return; 314251875Speter } 315251875Speter 316251875Speter while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 317251875Speter if (fwrite(buf, 1, nr, ofp) != nr) { 318251875Speter cwarn("%s", out); 319251875Speter goto err; 320251875Speter } 321251875Speter 322251875Speter if (ferror(ifp) || fclose(ifp)) { 323251875Speter cwarn("%s", in); 324251875Speter goto err; 325251875Speter } 326251875Speter ifp = NULL; 327251875Speter 328251875Speter if (fclose(ofp)) { 329251875Speter cwarn("%s", out); 330251875Speter goto err; 331251875Speter } 332251875Speter 333251875Speter if (isreg) { 334251875Speter setfile(out, &sb); 335251875Speter 336251875Speter if (unlink(in)) 337251875Speter cwarn("%s", in); 338251875Speter } 339251875Speter return; 340251875Speter 341251875Spetererr: if (ofp) { 342251875Speter if (oreg) 343251875Speter (void)unlink(out); 344251875Speter (void)fclose(ofp); 345251875Speter } 346251875Speter if (ifp) 347251875Speter (void)fclose(ifp); 348251875Speter} 349251875Speter 350251875Speterstatic void 351251875Spetersetfile(const char *name, struct stat *fs) 352251875Speter{ 353251875Speter static struct timespec tspec[2]; 354251875Speter 355251875Speter fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 356251875Speter 357251875Speter tspec[0] = fs->st_atim; 358251875Speter tspec[1] = fs->st_mtim; 359251875Speter if (utimensat(AT_FDCWD, name, tspec, 0)) 360251875Speter cwarn("utimensat: %s", name); 361251875Speter 362251875Speter /* 363251875Speter * Changing the ownership probably won't succeed, unless we're root 364251875Speter * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 365251875Speter * the mode; current BSD behavior is to remove all setuid bits on 366251875Speter * chown. If chown fails, lose setuid/setgid bits. 367251875Speter */ 368251875Speter if (chown(name, fs->st_uid, fs->st_gid)) { 369251875Speter if (errno != EPERM) 370251875Speter cwarn("chown: %s", name); 371251875Speter fs->st_mode &= ~(S_ISUID|S_ISGID); 372251875Speter } 373251875Speter if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP) 374251875Speter cwarn("chmod: %s", name); 375251875Speter 376251875Speter if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP) 377251875Speter cwarn("chflags: %s", name); 378251875Speter} 379251875Speter 380251875Speterstatic int 381251875Speterpermission(const char *fname) 382251875Speter{ 383251875Speter int ch, first; 384251875Speter 385251875Speter if (!isatty(fileno(stderr))) 386251875Speter return (0); 387251875Speter (void)fprintf(stderr, "overwrite %s? ", fname); 388251875Speter first = ch = getchar(); 389251875Speter while (ch != '\n' && ch != EOF) 390251875Speter ch = getchar(); 391251875Speter return (first == 'y'); 392251875Speter} 393251875Speter 394251875Speterstatic void 395251875Speterusage(int iscompress) 396251875Speter{ 397251875Speter if (iscompress) 398251875Speter (void)fprintf(stderr, 399251875Speter "usage: compress [-cfv] [-b bits] [file ...]\n"); 400251875Speter else 401251875Speter (void)fprintf(stderr, 402251875Speter "usage: uncompress [-c] [-b bits] [file ...]\n"); 403251875Speter exit(1); 404251875Speter} 405251875Speter 406251875Speterstatic void 407251875Spetercwarnx(const char *fmt, ...) 408251875Speter{ 409251875Speter va_list ap; 410251875Speter 411251875Speter va_start(ap, fmt); 412251875Speter vwarnx(fmt, ap); 413251875Speter va_end(ap); 414251875Speter eval = 1; 415251875Speter} 416251875Speter 417251875Speterstatic void 418251875Spetercwarn(const char *fmt, ...) 419251875Speter{ 420251875Speter va_list ap; 421251875Speter 422251875Speter va_start(ap, fmt); 423251875Speter vwarn(fmt, ap); 424251875Speter va_end(ap); 425251875Speter eval = 1; 426251875Speter} 427251875Speter