1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#) Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. 34 * @(#)compress.c 8.2 (Berkeley) 1/7/94 35 * $FreeBSD: src/usr.bin/compress/compress.c,v 1.7.6.5 2002/07/16 00:56:04 tjr Exp $ 36 * $DragonFly: src/usr.bin/compress/compress.c,v 1.2 2003/06/17 04:29:25 dillon Exp $ 37 */ 38 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <sys/time.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "zopen.h" 52 53 void compress(const char *, const char *, int); 54 void cwarn(const char *, ...) __printflike(1, 2); 55 void cwarnx(const char *, ...) __printflike(1, 2); 56 void decompress(const char *, const char *, int); 57 int permission(const char *); 58 void setfile(const char *, struct stat *); 59 void usage(int); 60 61 int eval, force, verbose; 62 63 int 64 main(argc, argv) 65 int argc; 66 char *argv[]; 67 { 68 enum {COMPRESS, DECOMPRESS} style; 69 size_t len; 70 int bits, cat, ch; 71 char *p, newname[MAXPATHLEN]; 72 73 cat = 0; 74 if ((p = rindex(argv[0], '/')) == NULL) 75 p = argv[0]; 76 else 77 ++p; 78 if (!strcmp(p, "uncompress")) 79 style = DECOMPRESS; 80 else if (!strcmp(p, "compress")) 81 style = COMPRESS; 82 else if (!strcmp(p, "zcat")) { 83 cat = 1; 84 style = DECOMPRESS; 85 } else 86 errx(1, "unknown program name"); 87 88 bits = 0; 89 while ((ch = getopt(argc, argv, "b:cdfv")) != -1) 90 switch(ch) { 91 case 'b': 92 bits = strtol(optarg, &p, 10); 93 if (*p) 94 errx(1, "illegal bit count -- %s", optarg); 95 break; 96 case 'c': 97 cat = 1; 98 break; 99 case 'd': /* Backward compatible. */ 100 style = DECOMPRESS; 101 break; 102 case 'f': 103 force = 1; 104 break; 105 case 'v': 106 verbose = 1; 107 break; 108 case '?': 109 default: 110 usage(style == COMPRESS); 111 } 112 argc -= optind; 113 argv += optind; 114 115 if (argc == 0) { 116 switch(style) { 117 case COMPRESS: 118 (void)compress("/dev/stdin", "/dev/stdout", bits); 119 break; 120 case DECOMPRESS: 121 (void)decompress("/dev/stdin", "/dev/stdout", bits); 122 break; 123 } 124 exit (eval); 125 } 126 127 if (cat == 1 && argc > 1) 128 errx(1, "the -c option permits only a single file argument"); 129 130 for (; *argv; ++argv) 131 switch(style) { 132 case COMPRESS: 133 if (strcmp(*argv, "-") == 0) { 134 compress("/dev/stdin", "/dev/stdout", bits); 135 break; 136 } else if (cat) { 137 compress(*argv, "/dev/stdout", bits); 138 break; 139 } 140 if ((p = rindex(*argv, '.')) != NULL && 141 !strcmp(p, ".Z")) { 142 cwarnx("%s: name already has trailing .Z", 143 *argv); 144 break; 145 } 146 len = strlen(*argv); 147 if (len > sizeof(newname) - 3) { 148 cwarnx("%s: name too long", *argv); 149 break; 150 } 151 memmove(newname, *argv, len); 152 newname[len] = '.'; 153 newname[len + 1] = 'Z'; 154 newname[len + 2] = '\0'; 155 compress(*argv, newname, bits); 156 break; 157 case DECOMPRESS: 158 if (strcmp(*argv, "-") == 0) { 159 decompress("/dev/stdin", "/dev/stdout", bits); 160 break; 161 } 162 len = strlen(*argv); 163 if ((p = rindex(*argv, '.')) == NULL || 164 strcmp(p, ".Z")) { 165 if (len > sizeof(newname) - 3) { 166 cwarnx("%s: name too long", *argv); 167 break; 168 } 169 memmove(newname, *argv, len); 170 newname[len] = '.'; 171 newname[len + 1] = 'Z'; 172 newname[len + 2] = '\0'; 173 decompress(newname, 174 cat ? "/dev/stdout" : *argv, bits); 175 } else { 176 if (len - 2 > sizeof(newname) - 1) { 177 cwarnx("%s: name too long", *argv); 178 break; 179 } 180 memmove(newname, *argv, len - 2); 181 newname[len - 2] = '\0'; 182 decompress(*argv, 183 cat ? "/dev/stdout" : newname, bits); 184 } 185 break; 186 } 187 exit (eval); 188 } 189 190 void 191 compress(in, out, bits) 192 const char *in, *out; 193 int bits; 194 { 195 size_t nr; 196 struct stat isb, sb; 197 FILE *ifp, *ofp; 198 int exists, isreg, oreg; 199 u_char buf[1024]; 200 201 exists = !stat(out, &sb); 202 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 203 return; 204 isreg = oreg = !exists || S_ISREG(sb.st_mode); 205 206 ifp = ofp = NULL; 207 if ((ifp = fopen(in, "r")) == NULL) { 208 cwarn("%s", in); 209 return; 210 } 211 if (stat(in, &isb)) { /* DON'T FSTAT! */ 212 cwarn("%s", in); 213 goto err; 214 } 215 if (!S_ISREG(isb.st_mode)) 216 isreg = 0; 217 218 if ((ofp = zopen(out, "w", bits)) == NULL) { 219 cwarn("%s", out); 220 goto err; 221 } 222 while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 223 if (fwrite(buf, 1, nr, ofp) != nr) { 224 cwarn("%s", out); 225 goto err; 226 } 227 228 if (ferror(ifp) || fclose(ifp)) { 229 cwarn("%s", in); 230 goto err; 231 } 232 ifp = NULL; 233 234 if (fclose(ofp)) { 235 cwarn("%s", out); 236 goto err; 237 } 238 ofp = NULL; 239 240 if (isreg) { 241 if (stat(out, &sb)) { 242 cwarn("%s", out); 243 goto err; 244 } 245 246 if (!force && sb.st_size >= isb.st_size) { 247 if (verbose) 248 (void)fprintf(stderr, "%s: file would grow; left unmodified\n", 249 in); 250 eval = 2; 251 if (unlink(out)) 252 cwarn("%s", out); 253 goto err; 254 } 255 256 setfile(out, &isb); 257 258 if (unlink(in)) 259 cwarn("%s", in); 260 261 if (verbose) { 262 (void)fprintf(stderr, "%s: ", out); 263 if (isb.st_size > sb.st_size) 264 (void)fprintf(stderr, "%.0f%% compression\n", 265 ((float)sb.st_size / isb.st_size) * 100.0); 266 else 267 (void)fprintf(stderr, "%.0f%% expansion\n", 268 ((float)isb.st_size / sb.st_size) * 100.0); 269 } 270 } 271 return; 272 273 err: if (ofp) { 274 if (oreg) 275 (void)unlink(out); 276 (void)fclose(ofp); 277 } 278 if (ifp) 279 (void)fclose(ifp); 280 } 281 282 void 283 decompress(in, out, bits) 284 const char *in, *out; 285 int bits; 286 { 287 size_t nr; 288 struct stat sb; 289 FILE *ifp, *ofp; 290 int exists, isreg, oreg; 291 u_char buf[1024]; 292 293 exists = !stat(out, &sb); 294 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 295 return; 296 isreg = oreg = !exists || S_ISREG(sb.st_mode); 297 298 ifp = ofp = NULL; 299 if ((ofp = fopen(out, "w")) == NULL) { 300 cwarn("%s", out); 301 return; 302 } 303 304 if ((ifp = zopen(in, "r", bits)) == NULL) { 305 cwarn("%s", in); 306 goto err; 307 } 308 if (stat(in, &sb)) { 309 cwarn("%s", in); 310 goto err; 311 } 312 if (!S_ISREG(sb.st_mode)) 313 isreg = 0; 314 315 while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 316 if (fwrite(buf, 1, nr, ofp) != nr) { 317 cwarn("%s", out); 318 goto err; 319 } 320 321 if (ferror(ifp) || fclose(ifp)) { 322 cwarn("%s", in); 323 goto err; 324 } 325 ifp = NULL; 326 327 if (fclose(ofp)) { 328 cwarn("%s", out); 329 goto err; 330 } 331 332 if (isreg) { 333 setfile(out, &sb); 334 335 if (unlink(in)) 336 cwarn("%s", in); 337 } 338 return; 339 340 err: if (ofp) { 341 if (oreg) 342 (void)unlink(out); 343 (void)fclose(ofp); 344 } 345 if (ifp) 346 (void)fclose(ifp); 347 } 348 349 void 350 setfile(name, fs) 351 const char *name; 352 struct stat *fs; 353 { 354 static struct timeval tv[2]; 355 356 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 357 358 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 359 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 360 if (utimes(name, tv)) 361 cwarn("utimes: %s", name); 362 363 /* 364 * Changing the ownership probably won't succeed, unless we're root 365 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 366 * the mode; current BSD behavior is to remove all setuid bits on 367 * chown. If chown fails, lose setuid/setgid bits. 368 */ 369 if (chown(name, fs->st_uid, fs->st_gid)) { 370 if (errno != EPERM) 371 cwarn("chown: %s", name); 372 fs->st_mode &= ~(S_ISUID|S_ISGID); 373 } 374 if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP) 375 cwarn("chmod: %s", name); 376 377 if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP) 378 cwarn("chflags: %s", name); 379 } 380 381 int 382 permission(fname) 383 const char *fname; 384 { 385 int ch, first; 386 387 if (!isatty(fileno(stderr))) 388 return (0); 389 (void)fprintf(stderr, "overwrite %s? ", fname); 390 first = ch = getchar(); 391 while (ch != '\n' && ch != EOF) 392 ch = getchar(); 393 return (first == 'y'); 394 } 395 396 void 397 usage(iscompress) 398 int iscompress; 399 { 400 if (iscompress) 401 (void)fprintf(stderr, 402 "usage: compress [-cfv] [-b bits] [file ...]\n"); 403 else 404 (void)fprintf(stderr, 405 "usage: uncompress [-c] [-b bits] [file ...]\n"); 406 exit(1); 407 } 408 409 void 410 cwarnx(const char *fmt, ...) 411 { 412 va_list ap; 413 414 va_start(ap, fmt); 415 vwarnx(fmt, ap); 416 va_end(ap); 417 eval = 1; 418 } 419 420 void 421 cwarn(const char *fmt, ...) 422 { 423 va_list ap; 424 425 va_start(ap, fmt); 426 vwarn(fmt, ap); 427 va_end(ap); 428 eval = 1; 429 } 430