symbolic links take on user ownership of their containing directory
[unix-history] / usr / src / usr.bin / compress / compress.c
CommitLineData
5b35086d
KB
1/*-
2 * Copyright (c) 1992 The Regents of the University of California.
58777538
KB
3 * All rights reserved.
4 *
f15db449 5 * %sccs.include.redist.c%
58777538
KB
6 */
7
8#ifndef lint
9char copyright[] =
5b35086d 10"@(#) Copyright (c) 1992 The Regents of the University of California.\n\
58777538
KB
11 All rights reserved.\n";
12#endif /* not lint */
13
64d06f2f 14#ifndef lint
7dab01ce 15static char sccsid[] = "@(#)compress.c 5.24 (Berkeley) %G%";
58777538 16#endif /* not lint */
64d06f2f 17
e5f559d4 18#include <sys/param.h>
5b35086d 19#include <sys/time.h>
e5f559d4 20#include <sys/stat.h>
5b35086d 21
090e3718 22#include <errno.h>
e5f559d4 23#include <stdio.h>
e5f559d4
KB
24#include <stdlib.h>
25#include <string.h>
5b35086d 26#include <unistd.h>
a2de8fe3 27
7dab01ce
KB
28void compress __P((char *, char *, int));
29void decompress __P((char *, char *, int));
30void err __P((int, const char *, ...));
31int permission __P((char *));
32void setfile __P((char *, struct stat *));
33void usage __P((int));
64d06f2f 34
5b35086d
KB
35int eval, force, verbose;
36char *progname;
4eeb10d4 37
5b35086d 38int
e5f559d4
KB
39main(argc, argv)
40 int argc;
5b35086d 41 char *argv[];
64d06f2f 42{
5b35086d
KB
43 enum {COMPRESS, DECOMPRESS} style;
44 size_t len;
45 int bits, cat, ch;
46 char *p, newname[MAXPATHLEN];
e5f559d4 47
5b35086d
KB
48 if ((p = rindex(argv[0], '/')) == NULL)
49 p = argv[0];
e5f559d4 50 else
5b35086d
KB
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");
e5f559d4 61 }
64d06f2f 62
5b35086d
KB
63 bits = cat = 0;
64 while ((ch = getopt(argc, argv, "b:cdfv")) != EOF)
e5f559d4
KB
65 switch(ch) {
66 case 'b':
5b35086d
KB
67 bits = strtol(optarg, &p, 10);
68 if (*p)
69 err(1, "illegal bit count -- %s", optarg);
4eeb10d4 70 break;
e5f559d4 71 case 'c':
5b35086d 72 cat = 1;
e5f559d4 73 break;
5b35086d
KB
74 case 'd': /* Backward compatible. */
75 style = DECOMPRESS;
64d06f2f 76 break;
e5f559d4 77 case 'f':
a2de8fe3 78 force = 1;
64d06f2f 79 break;
e5f559d4 80 case 'v':
5b35086d 81 verbose = 1;
64d06f2f 82 break;
e5f559d4
KB
83 case '?':
84 default:
5b35086d 85 usage(style == COMPRESS);
64d06f2f 86 }
e5f559d4
KB
87 argc -= optind;
88 argv += optind;
89
5b35086d
KB
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;
64d06f2f 98 }
5b35086d
KB
99 exit (eval);
100 }
a2de8fe3 101
5b35086d
KB
102 if (cat == 1 && argc > 1)
103 err(1, "the -c option permits only a single file argument");
64d06f2f 104
5b35086d
KB
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);
7dab01ce
KB
131 if ((p = rindex(*argv, '.')) == NULL ||
132 strcmp(p, ".Z")) {
5b35086d
KB
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);
5b35086d
KB
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;
64d06f2f 154 }
5b35086d 155 exit (eval);
64d06f2f
KM
156}
157
5b35086d
KB
158void
159compress(in, out, bits)
160 char *in, *out;
161 int bits;
090e3718 162{
5b35086d 163 register int nr;
7dab01ce 164 struct stat isb, sb;
5b35086d 165 FILE *ifp, *ofp;
3a6d9f0d 166 int exists, isreg, oreg;
5b35086d
KB
167 u_char buf[1024];
168
169 exists = !stat(out, &sb);
170 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
171 return;
3a6d9f0d 172 isreg = oreg = !exists || S_ISREG(sb.st_mode);
5b35086d
KB
173
174 ifp = ofp = NULL;
175 if ((ifp = fopen(in, "r")) == NULL) {
176 err(0, "%s: %s", in, strerror(errno));
177 return;
64d06f2f 178 }
7dab01ce 179 if (stat(in, &isb)) { /* DON'T FSTAT! */
5b35086d
KB
180 err(0, "%s: %s", in, strerror(errno));
181 goto err;
64d06f2f 182 }
7dab01ce 183 if (!S_ISREG(isb.st_mode))
3a6d9f0d 184 isreg = 0;
64d06f2f 185
5b35086d
KB
186 if ((ofp = zopen(out, "w", bits)) == NULL) {
187 err(0, "%s: %s", out, strerror(errno));
188 goto err;
090e3718 189 }
5b35086d
KB
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 }
64d06f2f 195
5b35086d
KB
196 if (ferror(ifp) || fclose(ifp)) {
197 err(0, "%s: %s", in, strerror(errno));
198 goto err;
64d06f2f 199 }
5b35086d 200 ifp = NULL;
64d06f2f 201
5b35086d
KB
202 if (fclose(ofp)) {
203 err(0, "%s: %s", out, strerror(errno));
204 goto err;
64d06f2f 205 }
5b35086d 206 ofp = NULL;
64d06f2f 207
3a6d9f0d 208 if (isreg) {
5b35086d
KB
209 if (stat(out, &sb)) {
210 err(0, "%s: %s", out, strerror(errno));
211 goto err;
212 }
3a6d9f0d 213
7dab01ce 214 if (!force && sb.st_size >= isb.st_size) {
5b35086d
KB
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 }
64d06f2f 221
7dab01ce 222 setfile(out, &isb);
64d06f2f 223
7dab01ce 224 if (unlink(in))
3a6d9f0d 225 err(0, "%s: %s", in, strerror(errno));
64d06f2f 226
3a6d9f0d
KB
227 if (verbose) {
228 (void)printf("%s: ", out);
7dab01ce 229 if (isb.st_size > sb.st_size)
3a6d9f0d 230 (void)printf("%.0f%% compression\n",
7dab01ce 231 ((float)sb.st_size / isb.st_size) * 100.0);
3a6d9f0d
KB
232 else
233 (void)printf("%.0f%% expansion\n",
7dab01ce 234 ((float)isb.st_size / sb.st_size) * 100.0);
3a6d9f0d 235 }
64d06f2f 236 }
5b35086d 237 return;
64d06f2f 238
5b35086d
KB
239err: if (ofp) {
240 if (oreg)
241 (void)unlink(out);
242 (void)fclose(ofp);
243 }
244 if (ifp)
245 (void)fclose(ifp);
64d06f2f
KM
246}
247
5b35086d
KB
248void
249decompress(in, out, bits)
250 char *in, *out;
251 int bits;
64d06f2f 252{
5b35086d
KB
253 register int nr;
254 struct stat sb;
5b35086d 255 FILE *ifp, *ofp;
7dab01ce 256 int exists, isreg, oreg;
5b35086d
KB
257 u_char buf[1024];
258
7dab01ce
KB
259 exists = !stat(out, &sb);
260 if (!force && exists && S_ISREG(sb.st_mode) && !permission(out))
5b35086d 261 return;
7dab01ce 262 isreg = oreg = !exists || S_ISREG(sb.st_mode);
5b35086d
KB
263
264 ifp = ofp = NULL;
265 if ((ofp = fopen(out, "w")) == NULL) {
266 err(0, "%s: %s", out, strerror(errno));
267 return;
268 }
64d06f2f 269
5b35086d
KB
270 if ((ifp = zopen(in, "r", bits)) == NULL) {
271 err(0, "%s: %s", in, strerror(errno));
272 goto err;
64d06f2f 273 }
5b35086d
KB
274 if (stat(in, &sb)) {
275 err(0, "%s: %s", in, strerror(errno));
276 goto err;
64d06f2f 277 }
3a6d9f0d
KB
278 if (!S_ISREG(sb.st_mode))
279 isreg = 0;
5b35086d
KB
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;
4eeb10d4 285 }
4eeb10d4 286
5b35086d
KB
287 if (ferror(ifp) || fclose(ifp)) {
288 err(0, "%s: %s", in, strerror(errno));
289 goto err;
290 }
291 ifp = NULL;
64d06f2f 292
5b35086d
KB
293 if (fclose(ofp)) {
294 err(0, "%s: %s", out, strerror(errno));
295 goto err;
4eeb10d4 296 }
64d06f2f 297
3a6d9f0d 298 if (isreg) {
7dab01ce
KB
299 setfile(out, &sb);
300
3a6d9f0d
KB
301 if (unlink(in))
302 err(0, "%s: %s", in, strerror(errno));
3a6d9f0d 303 }
64d06f2f 304 return;
64d06f2f 305
5b35086d
KB
306err: if (ofp) {
307 if (oreg)
308 (void)unlink(out);
309 (void)fclose(ofp);
310 }
311 if (ifp)
312 (void)fclose(ifp);
64d06f2f 313}
64d06f2f 314
7dab01ce
KB
315void
316setfile(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
5b35086d
KB
347int
348permission(fname)
349 char *fname;
64d06f2f 350{
5b35086d
KB
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');
4eeb10d4
JL
360}
361
392fe950 362void
5b35086d
KB
363usage(iscompress)
364 int iscompress;
4eeb10d4 365{
5b35086d
KB
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);
64d06f2f
KM
373}
374
5b35086d
KB
375#if __STDC__
376#include <stdarg.h>
377#else
378#include <varargs.h>
4eeb10d4 379#endif
64d06f2f 380
5b35086d
KB
381void
382#if __STDC__
383err(int fatal, const char *fmt, ...)
384#else
385err(fatal, fmt, va_alist)
386 int fatal;
387 char *fmt;
388 va_dcl
389#endif
e5f559d4 390{
5b35086d
KB
391 va_list ap;
392#if __STDC__
393 va_start(ap, fmt);
e5f559d4 394#else
5b35086d 395 va_start(ap);
e5f559d4 396#endif
5b35086d
KB
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;
e5f559d4 404}