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