1 /* $OpenBSD: main.c,v 1.9 1998/03/10 16:34:03 mickey Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 static char copyright[] = 37 "@(#) Copyright (c) 1992, 1993\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; 43 #else 44 static char rcsid[] = "$OpenBSD: main.c,v 1.9 1998/03/10 16:34:03 mickey Exp $"; 45 #endif 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/time.h> 50 #include <sys/stat.h> 51 52 #include <err.h> 53 #include <errno.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <fcntl.h> 59 #include <paths.h> 60 #include "compress.h" 61 62 #define min(a,b) ((a) < (b)? (a) : (b)) 63 64 int pipin = 0, force = 0, verbose = 0, testmode = 0, list = 0, nosave = 0; 65 extern char *__progname; 66 67 struct compressor { 68 char *name; 69 char *suffix; 70 int (*check_header) __P((int, struct stat *, const char *)); 71 void *(*open) __P((int, const char *, int)); 72 int (*read) __P((void *, char *, int)); 73 int (*write) __P((void *, const char *, int)); 74 int (*close) __P((void *)); 75 } c_table[] = { 76 #define M_COMPRESS (&c_table[0]) 77 { "compress", ".Z", z_check_header, z_open, zread, zwrite, zclose }, 78 #define M_DEFLATE (&c_table[1]) 79 { "deflate", ".gz", gz_check_header, gz_open, gz_read, gz_write, gz_close }, 80 { NULL } 81 }; 82 83 int permission __P((char *)); 84 void setfile __P((char *, struct stat *)); 85 void usage __P((void)); 86 int compress 87 __P((const char *, const char *, register struct compressor *, int)); 88 int decompress 89 __P((const char *, const char *, register struct compressor *, int)); 90 struct compressor *check_method __P((int, const char *)); 91 92 struct stat sb, osb; 93 94 int 95 main(argc, argv) 96 int argc; 97 char *argv[]; 98 { 99 int ch, bits, cat, decomp, error; 100 struct compressor *method; 101 int exists, isreg, oreg; 102 char *infile, outfile[MAXPATHLEN+4], suffix[16]; 103 char *p; 104 int rc = 0; 105 106 bits = cat = decomp = 0; 107 p = __progname; 108 if (p[0] == 'g') { 109 method = M_DEFLATE; 110 p++; 111 } else 112 method = M_COMPRESS; 113 114 decomp = 0; 115 if (!strcmp(p, "zcat")) { 116 decomp++; 117 cat++; 118 } else { 119 if (p[0] == 'u' && p[1] == 'n') { 120 p += 2; 121 decomp++; 122 } 123 124 if (strcmp(p, "zip") && 125 strcmp(p, "compress")) 126 errx(1, "unknown program name"); 127 } 128 129 outfile[0] = '\0'; 130 while ((ch = getopt(argc, argv, "0123456789b:cdfghlnOo:qStv")) != -1) 131 switch(ch) { 132 case '0': 133 case '1': 134 case '2': 135 case '3': 136 case '4': 137 case '5': 138 case '6': 139 case '7': 140 case '8': 141 case '9': 142 method = M_DEFLATE; 143 bits = ch - '0'; 144 break; 145 case 'b': 146 bits = strtol(optarg, &p, 10); 147 /* 148 * POSIX 1002.3 says 9 <= bits <= 14 for portable 149 * apps, but says the implementation may allow 150 * greater. 151 */ 152 if (*p) 153 errx(1, "illegal bit count -- %s", optarg); 154 break; 155 case 'c': 156 cat++; 157 break; 158 case 'd': /* Backward compatible. */ 159 decomp++; 160 break; 161 case 'f': 162 force++; 163 break; 164 case 'g': 165 method = M_DEFLATE; 166 break; 167 case 'l': 168 list++; 169 break; 170 case 'L': 171 fputs(copyright, stderr); 172 case 'n': 173 nosave++; 174 break; 175 case 'N': 176 nosave = 0; 177 break; 178 case 'O': 179 method = M_COMPRESS; 180 break; 181 case 'o': 182 strncpy(outfile, optarg, sizeof(outfile)-1); 183 break; 184 case 'q': 185 verbose = -1; 186 break; 187 case 'S': 188 p = suffix; 189 if (optarg[0] != '.') 190 *p++ = '.'; 191 strncpy(p, optarg, sizeof(suffix) - (p - suffix) - 1); 192 break; 193 case 't': 194 testmode++; 195 break; 196 case 'v': 197 verbose++; 198 break; 199 case 'h': 200 case '?': 201 default: 202 usage(); 203 } 204 argc -= optind; 205 argv += optind; 206 207 do { 208 if (*argv != NULL) { 209 infile = *argv; 210 if (outfile[0] == '\0') { 211 if (!decomp && !cat && outfile[0] == '\0') { 212 int len; 213 char *p; 214 215 snprintf(outfile, sizeof(outfile), 216 "%s%s", infile, 217 method->suffix); 218 219 len = strlen(outfile); 220 if (len > MAXPATHLEN) { 221 errx(1, "pathname%s too long", 222 method->suffix); 223 } 224 225 p = strrchr(outfile, '/'); 226 if (p == NULL) p = outfile; 227 len = strlen(p); 228 if (len > NAME_MAX) { 229 errx(1, "filename%s too long", 230 method->suffix); 231 } 232 } else if (decomp && !cat) { 233 char *p = strrchr(infile, '.'); 234 if (p != NULL) 235 for (method = &c_table[0]; 236 method->name != NULL && 237 !strcmp(p, method->suffix); 238 method++) 239 ; 240 if (method->name != NULL) { 241 int l = min(sizeof(outfile), 242 (p - infile)); 243 strncpy(outfile, infile, l); 244 outfile[l] = '\0'; 245 } 246 } 247 } 248 } else { 249 infile = "/dev/stdin"; 250 pipin++; 251 } 252 253 if (testmode) 254 strcpy(outfile, _PATH_DEVNULL); 255 else if (cat || outfile[0] == '\0') { 256 strcpy(outfile, "/dev/stdout"); 257 cat++; 258 } 259 260 exists = !stat(outfile, &sb); 261 if (!force && exists && S_ISREG(sb.st_mode) && 262 !permission(outfile)) { 263 argv++; 264 continue; 265 } 266 isreg = oreg = !exists || S_ISREG(sb.st_mode); 267 268 if (stat(infile, &sb) != 0 && verbose >= 0) 269 err(1, infile); 270 271 if (!S_ISREG(sb.st_mode)) 272 isreg = 0; 273 274 if (verbose > 0) 275 fprintf(stderr, "%s:\t", infile); 276 277 error = (decomp? decompress: compress) 278 (infile, outfile, method, bits); 279 280 if (!error && isreg && stat(outfile, &osb) == 0) { 281 282 if (!force && !decomp && osb.st_size >= sb.st_size) { 283 if (verbose > 0) 284 fprintf(stderr, "file would grow; " 285 "left unmodified\n"); 286 error = 1; 287 rc = 2; 288 } else { 289 290 setfile(outfile, &sb); 291 292 if (unlink(infile) && verbose >= 0) 293 warn("%s", infile); 294 295 if (verbose > 0) { 296 u_int ratio; 297 ratio = (1000*osb.st_size)/sb.st_size; 298 fprintf(stderr, "%u", ratio / 10); 299 if (ratio % 10) 300 fprintf(stderr, ".%u", 301 ratio % 10); 302 fputc('%', stderr); 303 fputc(' ', stderr); 304 } 305 } 306 } 307 308 if (error && oreg && unlink(outfile) && errno != ENOENT && 309 verbose >= 0) 310 warn("%s", outfile); 311 else if (!error && verbose > 0) 312 fputs("OK\n", stderr); 313 314 outfile[0] = '\0'; 315 if (*argv != NULL) 316 argv++; 317 318 } while (*argv != NULL); 319 320 return (rc); 321 } 322 323 int 324 compress(in, out, method, bits) 325 const char *in; 326 const char *out; 327 register struct compressor *method; 328 int bits; 329 { 330 register int ifd; 331 int ofd; 332 register void *cookie; 333 register size_t nr; 334 u_char buf[Z_BUFSIZE]; 335 int error; 336 337 error = 0; 338 cookie = NULL; 339 340 if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) { 341 if (verbose >= 0) 342 warn("%s", out); 343 return -1; 344 } 345 346 if (method != M_COMPRESS && !force && isatty(ofd)) { 347 if (verbose >= 0) 348 warnx("%s: won't write compressed data to terminal", 349 out); 350 return -1; 351 } 352 353 if ((ifd = open(in, O_RDONLY)) >= 0 && 354 (cookie = (*method->open)(ofd, "w", bits)) != NULL) { 355 356 while ((nr = read(ifd, buf, sizeof(buf))) > 0) 357 if ((method->write)(cookie, buf, nr) != nr) { 358 if (verbose >= 0) 359 warn("%s", out); 360 error++; 361 break; 362 } 363 } 364 365 if (ifd < 0 || close(ifd) || nr < 0) { 366 if (!error && verbose >= 0) 367 warn("%s", in); 368 error++; 369 } 370 371 if (cookie == NULL || (method->close)(cookie)) { 372 if (!error && verbose >= 0) 373 warn("%s", out); 374 error++; 375 (void) close(ofd); 376 } 377 378 return error? -1 : 0; 379 } 380 381 struct compressor * 382 check_method(fd, out) 383 int fd; 384 const char *out; 385 { 386 register struct compressor *method; 387 388 for (method = &c_table[0]; 389 method->name != NULL && 390 !(*method->check_header)(fd, &sb, out); 391 method++) 392 ; 393 394 if (method->name == NULL) 395 method = NULL; 396 397 return method; 398 } 399 400 int 401 decompress(in, out, method, bits) 402 const char *in; 403 const char *out; 404 register struct compressor *method; 405 int bits; 406 { 407 int ifd; 408 register int ofd; 409 register void *cookie; 410 register size_t nr; 411 u_char buf[Z_BUFSIZE]; 412 int error; 413 414 error = 0; 415 cookie = NULL; 416 417 if ((ifd = open(in, O_RDONLY)) < 0) { 418 if (verbose >= 0) 419 warn("%s", in); 420 return -1; 421 } 422 423 if (!force && isatty(ifd)) { 424 if (verbose >= 0) 425 warnx("%s: won't read compressed data from terminal", 426 in); 427 close (ifd); 428 return -1; 429 } 430 431 if (!pipin && (method = check_method(ifd, out)) == NULL) { 432 if (verbose >= 0) 433 warnx("%s: unrecognized file format", in); 434 return -1; 435 } 436 437 if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) >= 0 && 438 (cookie = (*method->open)(ifd, "r", bits)) != NULL) { 439 440 while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) 441 if (write(ofd, buf, nr) != nr) { 442 if (verbose >= 0) 443 warn("%s", out); 444 error++; 445 break; 446 } 447 } 448 449 if (ofd < 0 || close(ofd)) { 450 if (!error && verbose >= 0) 451 warn("%s", out); 452 error++; 453 } 454 455 if (cookie == NULL || (method->close)(cookie) || nr < 0) { 456 if (!error && verbose >= 0) 457 warn("%s", in); 458 error++; 459 (void) close (ifd); 460 } 461 462 return error; 463 } 464 465 void 466 setfile(name, fs) 467 char *name; 468 register struct stat *fs; 469 { 470 static struct timeval tv[2]; 471 472 fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 473 474 TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); 475 TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); 476 if (utimes(name, tv)) 477 warn("utimes: %s", name); 478 479 /* 480 * Changing the ownership probably won't succeed, unless we're root 481 * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 482 * the mode; current BSD behavior is to remove all setuid bits on 483 * chown. If chown fails, lose setuid/setgid bits. 484 */ 485 if (chown(name, fs->st_uid, fs->st_gid)) { 486 if (errno != EPERM) 487 warn("chown: %s", name); 488 fs->st_mode &= ~(S_ISUID|S_ISGID); 489 } 490 if (chmod(name, fs->st_mode)) 491 warn("chown: %s", name); 492 493 if (fs->st_flags && chflags(name, fs->st_flags)) 494 warn("chflags: %s", name); 495 } 496 497 int 498 permission(fname) 499 char *fname; 500 { 501 int ch, first; 502 503 if (!isatty(fileno(stderr))) 504 return (0); 505 (void)fprintf(stderr, "overwrite %s? ", fname); 506 first = ch = getchar(); 507 while (ch != '\n' && ch != EOF) 508 ch = getchar(); 509 return (first == 'y'); 510 } 511 512 void 513 usage() 514 { 515 fprintf(stderr, 516 "usage: %s [-cdfghlnOtqv] [-b <bits>] [-[0-9]] [file ...]\n", 517 __progname); 518 exit(1); 519 } 520 521