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