156969Sbostic /*-
261954Sbostic * Copyright (c) 1992, 1993
361954Sbostic * The Regents of the University of California. All rights reserved.
435808Sbostic *
542724Sbostic * %sccs.include.redist.c%
635808Sbostic */
735808Sbostic
822592Smckusick #ifndef lint
961954Sbostic static char copyright[] =
1061954Sbostic "@(#) Copyright (c) 1992, 1993\n\
1161954Sbostic The Regents of the University of California. All rights reserved.\n";
1235808Sbostic #endif /* not lint */
1322592Smckusick
1435808Sbostic #ifndef lint
15*65562Sbostic static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 01/07/94";
1635808Sbostic #endif /* not lint */
1735808Sbostic
1847441Sbostic #include <sys/param.h>
1956969Sbostic #include <sys/time.h>
2047441Sbostic #include <sys/stat.h>
2156969Sbostic
2259419Sbostic #include <err.h>
2347444Sbostic #include <errno.h>
2447441Sbostic #include <stdio.h>
2547441Sbostic #include <stdlib.h>
2647441Sbostic #include <string.h>
2756969Sbostic #include <unistd.h>
2847441Sbostic
2959419Sbostic #ifdef __STDC__
3059419Sbostic #include <stdarg.h>
3159419Sbostic #else
3259419Sbostic #include <varargs.h>
3359419Sbostic #endif
3459419Sbostic
3556976Sbostic void compress __P((char *, char *, int));
3659419Sbostic void cwarn __P((const char *, ...));
3759419Sbostic void cwarnx __P((const char *, ...));
3856976Sbostic void decompress __P((char *, char *, int));
3956976Sbostic int permission __P((char *));
4056976Sbostic void setfile __P((char *, struct stat *));
4156976Sbostic void usage __P((int));
4222592Smckusick
4356969Sbostic int eval, force, verbose;
4422730Smckusick
4556969Sbostic int
main(argc,argv)4647441Sbostic main(argc, argv)
4747441Sbostic int argc;
4856969Sbostic char *argv[];
4922592Smckusick {
5056969Sbostic enum {COMPRESS, DECOMPRESS} style;
5156969Sbostic size_t len;
5256969Sbostic int bits, cat, ch;
5356969Sbostic char *p, newname[MAXPATHLEN];
5422592Smckusick
5556969Sbostic if ((p = rindex(argv[0], '/')) == NULL)
5656969Sbostic p = argv[0];
5747441Sbostic else
5856969Sbostic ++p;
5959425Sbostic if (!strcmp(p, "uncompress"))
6056969Sbostic style = DECOMPRESS;
6159425Sbostic else if (!strcmp(p, "compress"))
6256969Sbostic style = COMPRESS;
6359425Sbostic else
6459419Sbostic errx(1, "unknown program name");
6522592Smckusick
6656969Sbostic bits = cat = 0;
6756969Sbostic while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
6847441Sbostic switch(ch) {
6947441Sbostic case 'b':
7056969Sbostic bits = strtol(optarg, &p, 10);
7156969Sbostic if (*p)
7259419Sbostic errx(1, "illegal bit count -- %s", optarg);
7347441Sbostic break;
7447441Sbostic case 'c':
7556969Sbostic cat = 1;
7647441Sbostic break;
7756969Sbostic case 'd': /* Backward compatible. */
7856969Sbostic style = DECOMPRESS;
7922592Smckusick break;
8047441Sbostic case 'f':
8122730Smckusick force = 1;
8222592Smckusick break;
8356969Sbostic case 'v':
8447441Sbostic verbose = 1;
8522592Smckusick break;
8647441Sbostic case '?':
8747441Sbostic default:
8856969Sbostic usage(style == COMPRESS);
8922592Smckusick }
9047441Sbostic argc -= optind;
9147441Sbostic argv += optind;
9222592Smckusick
9356969Sbostic if (argc == 0) {
9456969Sbostic switch(style) {
9556969Sbostic case COMPRESS:
9656969Sbostic (void)compress("/dev/stdin", "/dev/stdout", bits);
9756969Sbostic break;
9856969Sbostic case DECOMPRESS:
9956969Sbostic (void)decompress("/dev/stdin", "/dev/stdout", bits);
10056969Sbostic break;
10156969Sbostic }
10256969Sbostic exit (eval);
10356969Sbostic }
10422592Smckusick
10556969Sbostic if (cat == 1 && argc > 1)
10659419Sbostic errx(1, "the -c option permits only a single file argument");
10747441Sbostic
10856969Sbostic for (; *argv; ++argv)
10956969Sbostic switch(style) {
11056969Sbostic case COMPRESS:
11156969Sbostic if (cat) {
11256969Sbostic compress(*argv, "/dev/stdout", bits);
11356969Sbostic break;
11422592Smckusick }
11556969Sbostic if ((p = rindex(*argv, '.')) != NULL &&
11656969Sbostic !strcmp(p, ".Z")) {
11759419Sbostic cwarnx("%s: name already has trailing .Z",
11856969Sbostic *argv);
11956969Sbostic break;
12056969Sbostic }
12156969Sbostic len = strlen(*argv);
12256969Sbostic if (len > sizeof(newname) - 3) {
12359419Sbostic cwarnx("%s: name too long", *argv);
12456969Sbostic break;
12556969Sbostic }
12656969Sbostic memmove(newname, *argv, len);
12756969Sbostic newname[len] = '.';
12856969Sbostic newname[len + 1] = 'Z';
12956969Sbostic newname[len + 2] = '\0';
13056969Sbostic compress(*argv, newname, bits);
13156969Sbostic break;
13256969Sbostic case DECOMPRESS:
13356969Sbostic len = strlen(*argv);
13456976Sbostic if ((p = rindex(*argv, '.')) == NULL ||
13556976Sbostic strcmp(p, ".Z")) {
13656969Sbostic if (len > sizeof(newname) - 3) {
13759419Sbostic cwarnx("%s: name too long", *argv);
13856969Sbostic break;
13956969Sbostic }
14056969Sbostic memmove(newname, *argv, len);
14156969Sbostic newname[len] = '.';
14256969Sbostic newname[len + 1] = 'Z';
14356969Sbostic newname[len + 2] = '\0';
14456969Sbostic decompress(newname,
14556969Sbostic cat ? "/dev/stdout" : *argv, bits);
14656969Sbostic } else {
14756969Sbostic if (len - 2 > sizeof(newname) - 1) {
14859419Sbostic cwarnx("%s: name too long", *argv);
14956969Sbostic break;
15056969Sbostic }
15156969Sbostic memmove(newname, *argv, len - 2);
15256969Sbostic newname[len - 2] = '\0';
15356969Sbostic decompress(*argv,
15456969Sbostic cat ? "/dev/stdout" : newname, bits);
15556969Sbostic }
15656969Sbostic break;
15722592Smckusick }
15856969Sbostic exit (eval);
15922592Smckusick }
16022592Smckusick
16156969Sbostic void
compress(in,out,bits)16256969Sbostic compress(in, out, bits)
16356969Sbostic char *in, *out;
16456969Sbostic int bits;
16547444Sbostic {
16656969Sbostic register int nr;
16756976Sbostic struct stat isb, sb;
16856969Sbostic FILE *ifp, *ofp;
16956973Sbostic int exists, isreg, oreg;
17056969Sbostic u_char buf[1024];
17122592Smckusick
17256969Sbostic exists = !stat(out, &sb);
17356969Sbostic if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
17456969Sbostic return;
17556973Sbostic isreg = oreg = !exists || S_ISREG(sb.st_mode);
17622592Smckusick
17756969Sbostic ifp = ofp = NULL;
17856969Sbostic if ((ifp = fopen(in, "r")) == NULL) {
17959419Sbostic cwarn("%s", in);
18056969Sbostic return;
18122592Smckusick }
18256976Sbostic if (stat(in, &isb)) { /* DON'T FSTAT! */
18359419Sbostic cwarn("%s", in);
18456969Sbostic goto err;
18522592Smckusick }
18656976Sbostic if (!S_ISREG(isb.st_mode))
18756973Sbostic isreg = 0;
18822592Smckusick
18956969Sbostic if ((ofp = zopen(out, "w", bits)) == NULL) {
19059419Sbostic cwarn("%s", out);
19156969Sbostic goto err;
19256969Sbostic }
19356969Sbostic while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
19456969Sbostic if (fwrite(buf, 1, nr, ofp) != nr) {
19559419Sbostic cwarn("%s", out);
19656969Sbostic goto err;
19756969Sbostic }
19822592Smckusick
19956969Sbostic if (ferror(ifp) || fclose(ifp)) {
20059419Sbostic cwarn("%s", in);
20156969Sbostic goto err;
20256969Sbostic }
20356969Sbostic ifp = NULL;
20422592Smckusick
20556969Sbostic if (fclose(ofp)) {
20659419Sbostic cwarn("%s", out);
20756969Sbostic goto err;
20856969Sbostic }
20956969Sbostic ofp = NULL;
21022592Smckusick
21156973Sbostic if (isreg) {
21256969Sbostic if (stat(out, &sb)) {
21359419Sbostic cwarn("%s", out);
21456969Sbostic goto err;
21556969Sbostic }
21656973Sbostic
21756976Sbostic if (!force && sb.st_size >= isb.st_size) {
21856969Sbostic if (verbose)
21956969Sbostic (void)printf("%s: file would grow; left unmodified\n", in);
22056969Sbostic if (unlink(out))
22159419Sbostic cwarn("%s", out);
22256969Sbostic goto err;
22356969Sbostic }
22422592Smckusick
22556976Sbostic setfile(out, &isb);
22656976Sbostic
22756973Sbostic if (unlink(in))
22859419Sbostic cwarn("%s", in);
22922592Smckusick
23056973Sbostic if (verbose) {
23156973Sbostic (void)printf("%s: ", out);
23256976Sbostic if (isb.st_size > sb.st_size)
23356973Sbostic (void)printf("%.0f%% compression\n",
23456976Sbostic ((float)sb.st_size / isb.st_size) * 100.0);
23556973Sbostic else
23656973Sbostic (void)printf("%.0f%% expansion\n",
23756976Sbostic ((float)isb.st_size / sb.st_size) * 100.0);
23856973Sbostic }
23922592Smckusick }
24056969Sbostic return;
24122592Smckusick
24256969Sbostic err: if (ofp) {
24356969Sbostic if (oreg)
24456969Sbostic (void)unlink(out);
24556969Sbostic (void)fclose(ofp);
24622592Smckusick }
24756969Sbostic if (ifp)
24856969Sbostic (void)fclose(ifp);
24922592Smckusick }
25022592Smckusick
25156969Sbostic void
decompress(in,out,bits)25256969Sbostic decompress(in, out, bits)
25356969Sbostic char *in, *out;
25456969Sbostic int bits;
25556969Sbostic {
25656969Sbostic register int nr;
25756969Sbostic struct stat sb;
25856969Sbostic FILE *ifp, *ofp;
25956976Sbostic int exists, isreg, oreg;
26056969Sbostic u_char buf[1024];
26122730Smckusick
26256976Sbostic exists = !stat(out, &sb);
26356976Sbostic if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
26456969Sbostic return;
26556976Sbostic isreg = oreg = !exists || S_ISREG(sb.st_mode);
26622592Smckusick
26756969Sbostic ifp = ofp = NULL;
26856969Sbostic if ((ofp = fopen(out, "w")) == NULL) {
26959419Sbostic cwarn("%s", out);
27056969Sbostic return;
27156969Sbostic }
27247431Sbostic
27356969Sbostic if ((ifp = zopen(in, "r", bits)) == NULL) {
27459419Sbostic cwarn("%s", in);
27556969Sbostic goto err;
27622592Smckusick }
27756969Sbostic if (stat(in, &sb)) {
27859419Sbostic cwarn("%s", in);
27956969Sbostic goto err;
28022592Smckusick }
28156973Sbostic if (!S_ISREG(sb.st_mode))
28256973Sbostic isreg = 0;
28322592Smckusick
28456969Sbostic while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0)
28556969Sbostic if (fwrite(buf, 1, nr, ofp) != nr) {
28659419Sbostic cwarn("%s", out);
28756969Sbostic goto err;
28856969Sbostic }
28922592Smckusick
29056969Sbostic if (ferror(ifp) || fclose(ifp)) {
29159419Sbostic cwarn("%s", in);
29256969Sbostic goto err;
29322592Smckusick }
29456969Sbostic ifp = NULL;
29522592Smckusick
29656969Sbostic if (fclose(ofp)) {
29759419Sbostic cwarn("%s", out);
29856969Sbostic goto err;
29922592Smckusick }
30022592Smckusick
30156973Sbostic if (isreg) {
30256976Sbostic setfile(out, &sb);
30356976Sbostic
30456973Sbostic if (unlink(in))
30559419Sbostic cwarn("%s", in);
30656973Sbostic }
30756969Sbostic return;
30824830Slepreau
30956969Sbostic err: if (ofp) {
31056969Sbostic if (oreg)
31156969Sbostic (void)unlink(out);
31256969Sbostic (void)fclose(ofp);
31322592Smckusick }
31456969Sbostic if (ifp)
31556969Sbostic (void)fclose(ifp);
31622592Smckusick }
31722592Smckusick
31856976Sbostic void
setfile(name,fs)31956976Sbostic setfile(name, fs)
32056976Sbostic char *name;
32156976Sbostic register struct stat *fs;
32256976Sbostic {
32356976Sbostic static struct timeval tv[2];
32456976Sbostic
32556976Sbostic fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
32656976Sbostic
32756976Sbostic TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
32856976Sbostic TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
32956976Sbostic if (utimes(name, tv))
33059419Sbostic cwarn("utimes: %s", name);
33156976Sbostic
33256976Sbostic /*
33356976Sbostic * Changing the ownership probably won't succeed, unless we're root
33456976Sbostic * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
33556976Sbostic * the mode; current BSD behavior is to remove all setuid bits on
33656976Sbostic * chown. If chown fails, lose setuid/setgid bits.
33756976Sbostic */
33856976Sbostic if (chown(name, fs->st_uid, fs->st_gid)) {
33956976Sbostic if (errno != EPERM)
34059419Sbostic cwarn("chown: %s", name);
34156976Sbostic fs->st_mode &= ~(S_ISUID|S_ISGID);
34256976Sbostic }
34356976Sbostic if (chmod(name, fs->st_mode))
34459419Sbostic cwarn("chown: %s", name);
34556976Sbostic
34656976Sbostic if (chflags(name, fs->st_flags))
34759419Sbostic cwarn("chflags: %s", name);
34856976Sbostic }
34956976Sbostic
35024830Slepreau int
permission(fname)35156969Sbostic permission(fname)
35256969Sbostic char *fname;
35324830Slepreau {
35456969Sbostic int ch, first;
35556969Sbostic
35656969Sbostic if (!isatty(fileno(stderr)))
35756969Sbostic return (0);
35856969Sbostic (void)fprintf(stderr, "overwrite %s? ", fname);
35956969Sbostic first = ch = getchar();
36056969Sbostic while (ch != '\n' && ch != EOF)
36156969Sbostic ch = getchar();
36256969Sbostic return (first == 'y');
36324830Slepreau }
36422592Smckusick
36556969Sbostic void
usage(iscompress)36656969Sbostic usage(iscompress)
36756969Sbostic int iscompress;
36822592Smckusick {
36956969Sbostic if (iscompress)
37056969Sbostic (void)fprintf(stderr,
37156969Sbostic "usage: compress [-cfv] [-b bits] [file ...]\n");
37256969Sbostic else
37356969Sbostic (void)fprintf(stderr,
37456969Sbostic "usage: uncompress [-c] [-b bits] [file ...]\n");
37547444Sbostic exit(1);
37622592Smckusick }
37722592Smckusick
37859419Sbostic void
37956969Sbostic #if __STDC__
cwarnx(const char * fmt,...)38059419Sbostic cwarnx(const char *fmt, ...)
38156969Sbostic #else
38259419Sbostic cwarnx(fmt, va_alist)
38359419Sbostic int eval;
38459419Sbostic const char *fmt;
38559419Sbostic va_dcl
38656969Sbostic #endif
38759419Sbostic {
38859419Sbostic va_list ap;
38959419Sbostic #if __STDC__
39059419Sbostic va_start(ap, fmt);
39159419Sbostic #else
39259419Sbostic va_start(ap);
39359419Sbostic #endif
39459419Sbostic vwarnx(fmt, ap);
39559419Sbostic va_end(ap);
39659419Sbostic eval = 1;
39759419Sbostic }
39822592Smckusick
39939167Sbostic void
40056969Sbostic #if __STDC__
cwarn(const char * fmt,...)40159419Sbostic cwarn(const char *fmt, ...)
40256969Sbostic #else
40359419Sbostic cwarn(fmt, va_alist)
40459419Sbostic int eval;
40559419Sbostic const char *fmt;
40656969Sbostic va_dcl
40724830Slepreau #endif
40822592Smckusick {
40956969Sbostic va_list ap;
41056969Sbostic #if __STDC__
41156969Sbostic va_start(ap, fmt);
41247441Sbostic #else
41356969Sbostic va_start(ap);
41447441Sbostic #endif
41559419Sbostic vwarn(fmt, ap);
41656969Sbostic va_end(ap);
41756969Sbostic eval = 1;
41847441Sbostic }
419