Commit | Line | Data |
---|---|---|
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 | |
9 | char 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 | 15 | static 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 |
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)); | |
64d06f2f | 34 | |
5b35086d KB |
35 | int eval, force, verbose; |
36 | char *progname; | |
4eeb10d4 | 37 | |
5b35086d | 38 | int |
e5f559d4 KB |
39 | main(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 |
158 | void |
159 | compress(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 |
239 | err: 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 |
248 | void |
249 | decompress(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 |
306 | err: 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 |
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 | ||
5b35086d KB |
347 | int |
348 | permission(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 | 362 | void |
5b35086d KB |
363 | usage(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 |
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 | |
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 | } |