1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1992, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)compress.c 8.1 (Berkeley) 06/06/93"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/time.h> 20 #include <sys/stat.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #ifdef __STDC__ 30 #include <stdarg.h> 31 #else 32 #include <varargs.h> 33 #endif 34 35 void compress __P((char *, char *, int)); 36 void cwarn __P((const char *, ...)); 37 void cwarnx __P((const char *, ...)); 38 void decompress __P((char *, char *, int)); 39 void err __P((int, const char *, ...)); 40 int permission __P((char *)); 41 void setfile __P((char *, struct stat *)); 42 void usage __P((int)); 43 44 int eval, force, verbose; 45 46 int 47 main(argc, argv) 48 int argc; 49 char *argv[]; 50 { 51 enum {COMPRESS, DECOMPRESS} style; 52 size_t len; 53 int bits, cat, ch; 54 char *p, newname[MAXPATHLEN]; 55 56 if ((p = rindex(argv[0], '/')) == NULL) 57 p = argv[0]; 58 else 59 ++p; 60 if (!strcmp(p, "uncompress")) 61 style = DECOMPRESS; 62 else if (!strcmp(p, "compress")) 63 style = COMPRESS; 64 else 65 errx(1, "unknown program name"); 66 67 bits = cat = 0; 68 while ((ch = getopt(argc, argv, "b:cdfv")) != EOF) 69 switch(ch) { 70 case 'b': 71 bits = strtol(optarg, &p, 10); 72 if (*p) 73 errx(1, "illegal bit count -- %s", optarg); 74 break; 75 case 'c': 76 cat = 1; 77 break; 78 case 'd': /* Backward compatible. */ 79 style = DECOMPRESS; 80 break; 81 case 'f': 82 force = 1; 83 break; 84 case 'v': 85 verbose = 1; 86 break; 87 case '?': 88 default: 89 usage(style == COMPRESS); 90 } 91 argc -= optind; 92 argv += optind; 93 94 if (argc == 0) { 95 switch(style) { 96 case COMPRESS: 97 (void)compress("/dev/stdin", "/dev/stdout", bits); 98 break; 99 case DECOMPRESS: 100 (void)decompress("/dev/stdin", "/dev/stdout", bits); 101 break; 102 } 103 exit (eval); 104 } 105 106 if (cat == 1 && argc > 1) 107 errx(1, "the -c option permits only a single file argument"); 108 109 for (; *argv; ++argv) 110 switch(style) { 111 case COMPRESS: 112 if (cat) { 113 compress(*argv, "/dev/stdout", bits); 114 break; 115 } 116 if ((p = rindex(*argv, '.')) != NULL && 117 !strcmp(p, ".Z")) { 118 cwarnx("%s: name already has trailing .Z", 119 *argv); 120 break; 121 } 122 len = strlen(*argv); 123 if (len > sizeof(newname) - 3) { 124 cwarnx("%s: name too long", *argv); 125 break; 126 } 127 memmove(newname, *argv, len); 128 newname[len] = '.'; 129 newname[len + 1] = 'Z'; 130 newname[len + 2] = '\0'; 131 compress(*argv, newname, bits); 132 break; 133 case DECOMPRESS: 134 len = strlen(*argv); 135 if ((p = rindex(*argv, '.')) == NULL || 136 strcmp(p, ".Z")) { 137 if (len > sizeof(newname) - 3) { 138 cwarnx("%s: name too long", *argv); 139 break; 140 } 141 memmove(newname, *argv, len); 142 newname[len] = '.'; 143 newname[len + 1] = 'Z'; 144 newname[len + 2] = '\0'; 145 decompress(newname, 146 cat ? "/dev/stdout" : *argv, bits); 147 } else { 148 if (len - 2 > sizeof(newname) - 1) { 149 cwarnx("%s: name too long", *argv); 150 break; 151 } 152 memmove(newname, *argv, len - 2); 153 newname[len - 2] = '\0'; 154 decompress(*argv, 155 cat ? "/dev/stdout" : newname, bits); 156 } 157 break; 158 } 159 exit (eval); 160 } 161 162 void 163 compress(in, out, bits) 164 char *in, *out; 165 int bits; 166 { 167 register int nr; 168 struct stat isb, sb; 169 FILE *ifp, *ofp; 170 int exists, isreg, oreg; 171 u_char buf[1024]; 172 173 exists = !stat(out, &sb); 174 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 175 return; 176 isreg = oreg = !exists || S_ISREG(sb.st_mode); 177 178 ifp = ofp = NULL; 179 if ((ifp = fopen(in, "r")) == NULL) { 180 cwarn("%s", in); 181 return; 182 } 183 if (stat(in, &isb)) { /* DON'T FSTAT! */ 184 cwarn("%s", in); 185 goto err; 186 } 187 if (!S_ISREG(isb.st_mode)) 188 isreg = 0; 189 190 if ((ofp = zopen(out, "w", bits)) == NULL) { 191 cwarn("%s", out); 192 goto err; 193 } 194 while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 195 if (fwrite(buf, 1, nr, ofp) != nr) { 196 cwarn("%s", out); 197 goto err; 198 } 199 200 if (ferror(ifp) || fclose(ifp)) { 201 cwarn("%s", in); 202 goto err; 203 } 204 ifp = NULL; 205 206 if (fclose(ofp)) { 207 cwarn("%s", out); 208 goto err; 209 } 210 ofp = NULL; 211 212 if (isreg) { 213 if (stat(out, &sb)) { 214 cwarn("%s", out); 215 goto err; 216 } 217 218 if (!force && sb.st_size >= isb.st_size) { 219 if (verbose) 220 (void)printf("%s: file would grow; left unmodified\n", in); 221 if (unlink(out)) 222 cwarn("%s", out); 223 goto err; 224 } 225 226 setfile(out, &isb); 227 228 if (unlink(in)) 229 cwarn("%s", in); 230 231 if (verbose) { 232 (void)printf("%s: ", out); 233 if (isb.st_size > sb.st_size) 234 (void)printf("%.0f%% compression\n", 235 ((float)sb.st_size / isb.st_size) * 100.0); 236 else 237 (void)printf("%.0f%% expansion\n", 238 ((float)isb.st_size / sb.st_size) * 100.0); 239 } 240 } 241 return; 242 243 err: if (ofp) { 244 if (oreg) 245 (void)unlink(out); 246 (void)fclose(ofp); 247 } 248 if (ifp) 249 (void)fclose(ifp); 250 } 251 252 void 253 decompress(in, out, bits) 254 char *in, *out; 255 int bits; 256 { 257 register int nr; 258 struct stat sb; 259 FILE *ifp, *ofp; 260 int exists, isreg, oreg; 261 u_char buf[1024]; 262 263 exists = !stat(out, &sb); 264 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) 265 return; 266 isreg = oreg = !exists || S_ISREG(sb.st_mode); 267 268 ifp = ofp = NULL; 269 if ((ofp = fopen(out, "w")) == NULL) { 270 cwarn("%s", out); 271 return; 272 } 273 274 if ((ifp = zopen(in, "r", bits)) == NULL) { 275 cwarn("%s", in); 276 goto err; 277 } 278 if (stat(in, &sb)) { 279 cwarn("%s", in); 280 goto err; 281 } 282 if (!S_ISREG(sb.st_mode)) 283 isreg = 0; 284 285 while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) 286 if (fwrite(buf, 1, nr, ofp) != nr) { 287 cwarn("%s", out); 288 goto err; 289 } 290 291 if (ferror(ifp) || fclose(ifp)) { 292 cwarn("%s", in); 293 goto err; 294 } 295 ifp = NULL; 296 297 if (fclose(ofp)) { 298 cwarn("%s", out); 299 goto err; 300 } 301 302 if (isreg) { 303 setfile(out, &sb); 304 305 if (unlink(in)) 306 cwarn("%s", in); 307 } 308 return; 309 310 err: if (ofp) { 311 if (oreg) 312 (void)unlink(out); 313 (void)fclose(ofp); 314 } 315 if (ifp) 316 (void)fclose(ifp); 317 } 318 319 void 320 setfile(name, fs) 321 char *name; 322 register struct stat *fs; 323 { 324 static struct timeval tv[2]; 325 326 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 327 328 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 329 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 330 if (utimes(name, tv)) 331 cwarn("utimes: %s", name); 332 333 /* 334 * Changing the ownership probably won't succeed, unless we're root 335 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 336 * the mode; current BSD behavior is to remove all setuid bits on 337 * chown. If chown fails, lose setuid/setgid bits. 338 */ 339 if (chown(name, fs->st_uid, fs->st_gid)) { 340 if (errno != EPERM) 341 cwarn("chown: %s", name); 342 fs->st_mode &= ~(S_ISUID|S_ISGID); 343 } 344 if (chmod(name, fs->st_mode)) 345 cwarn("chown: %s", name); 346 347 if (chflags(name, fs->st_flags)) 348 cwarn("chflags: %s", name); 349 } 350 351 int 352 permission(fname) 353 char *fname; 354 { 355 int ch, first; 356 357 if (!isatty(fileno(stderr))) 358 return (0); 359 (void)fprintf(stderr, "overwrite %s? ", fname); 360 first = ch = getchar(); 361 while (ch != '\n' && ch != EOF) 362 ch = getchar(); 363 return (first == 'y'); 364 } 365 366 void 367 usage(iscompress) 368 int iscompress; 369 { 370 if (iscompress) 371 (void)fprintf(stderr, 372 "usage: compress [-cfv] [-b bits] [file ...]\n"); 373 else 374 (void)fprintf(stderr, 375 "usage: uncompress [-c] [-b bits] [file ...]\n"); 376 exit(1); 377 } 378 379 void 380 #if __STDC__ 381 cwarnx(const char *fmt, ...) 382 #else 383 cwarnx(fmt, va_alist) 384 int eval; 385 const char *fmt; 386 va_dcl 387 #endif 388 { 389 va_list ap; 390 #if __STDC__ 391 va_start(ap, fmt); 392 #else 393 va_start(ap); 394 #endif 395 vwarnx(fmt, ap); 396 va_end(ap); 397 eval = 1; 398 } 399 400 void 401 #if __STDC__ 402 cwarn(const char *fmt, ...) 403 #else 404 cwarn(fmt, va_alist) 405 int eval; 406 const char *fmt; 407 va_dcl 408 #endif 409 { 410 va_list ap; 411 #if __STDC__ 412 va_start(ap, fmt); 413 #else 414 va_start(ap); 415 #endif 416 vwarn(fmt, ap); 417 va_end(ap); 418 eval = 1; 419 } 420