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 |
3a6d9f0d | 15 | static char sccsid[] = "@(#)compress.c 5.23 (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 | |
5b35086d 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 usage __P((int)); | |
64d06f2f | 33 | |
5b35086d KB |
34 | int eval, force, verbose; |
35 | char *progname; | |
4eeb10d4 | 36 | |
5b35086d | 37 | int |
e5f559d4 KB |
38 | main(argc, argv) |
39 | int argc; | |
5b35086d | 40 | char *argv[]; |
64d06f2f | 41 | { |
5b35086d KB |
42 | enum {COMPRESS, DECOMPRESS} style; |
43 | size_t len; | |
44 | int bits, cat, ch; | |
45 | char *p, newname[MAXPATHLEN]; | |
e5f559d4 | 46 | |
5b35086d KB |
47 | if ((p = rindex(argv[0], '/')) == NULL) |
48 | p = argv[0]; | |
e5f559d4 | 49 | else |
5b35086d KB |
50 | ++p; |
51 | if (!strcmp(p, "uncompress")) { | |
52 | progname = "uncompress"; | |
53 | style = DECOMPRESS; | |
54 | } else if (!strcmp(p, "compress")) { | |
55 | progname = "compress"; | |
56 | style = COMPRESS; | |
57 | } else { | |
58 | progname = *argv; | |
59 | err(1, "unknown program name"); | |
e5f559d4 | 60 | } |
64d06f2f | 61 | |
5b35086d KB |
62 | bits = cat = 0; |
63 | while ((ch = getopt(argc, argv, "b:cdfv")) != EOF) | |
e5f559d4 KB |
64 | switch(ch) { |
65 | case 'b': | |
5b35086d KB |
66 | bits = strtol(optarg, &p, 10); |
67 | if (*p) | |
68 | err(1, "illegal bit count -- %s", optarg); | |
4eeb10d4 | 69 | break; |
e5f559d4 | 70 | case 'c': |
5b35086d | 71 | cat = 1; |
e5f559d4 | 72 | break; |
5b35086d KB |
73 | case 'd': /* Backward compatible. */ |
74 | style = DECOMPRESS; | |
64d06f2f | 75 | break; |
e5f559d4 | 76 | case 'f': |
a2de8fe3 | 77 | force = 1; |
64d06f2f | 78 | break; |
e5f559d4 | 79 | case 'v': |
5b35086d | 80 | verbose = 1; |
64d06f2f | 81 | break; |
e5f559d4 KB |
82 | case '?': |
83 | default: | |
5b35086d | 84 | usage(style == COMPRESS); |
64d06f2f | 85 | } |
e5f559d4 KB |
86 | argc -= optind; |
87 | argv += optind; | |
88 | ||
5b35086d KB |
89 | if (argc == 0) { |
90 | switch(style) { | |
91 | case COMPRESS: | |
92 | (void)compress("/dev/stdin", "/dev/stdout", bits); | |
93 | break; | |
94 | case DECOMPRESS: | |
95 | (void)decompress("/dev/stdin", "/dev/stdout", bits); | |
96 | break; | |
64d06f2f | 97 | } |
5b35086d KB |
98 | exit (eval); |
99 | } | |
a2de8fe3 | 100 | |
5b35086d KB |
101 | if (cat == 1 && argc > 1) |
102 | err(1, "the -c option permits only a single file argument"); | |
64d06f2f | 103 | |
5b35086d KB |
104 | for (; *argv; ++argv) |
105 | switch(style) { | |
106 | case COMPRESS: | |
107 | if (cat) { | |
108 | compress(*argv, "/dev/stdout", bits); | |
109 | break; | |
110 | } | |
111 | if ((p = rindex(*argv, '.')) != NULL && | |
112 | !strcmp(p, ".Z")) { | |
113 | err(0, "%s: name already has trailing .Z", | |
114 | *argv); | |
115 | break; | |
116 | } | |
117 | len = strlen(*argv); | |
118 | if (len > sizeof(newname) - 3) { | |
119 | err(0, "%s: name too long", *argv); | |
120 | break; | |
121 | } | |
122 | memmove(newname, *argv, len); | |
123 | newname[len] = '.'; | |
124 | newname[len + 1] = 'Z'; | |
125 | newname[len + 2] = '\0'; | |
126 | compress(*argv, newname, bits); | |
127 | break; | |
128 | case DECOMPRESS: | |
129 | len = strlen(*argv); | |
130 | if ((p = rindex(*argv, '.')) == NULL) { | |
131 | if (len > sizeof(newname) - 3) { | |
132 | err(0, "%s: name too long", *argv); | |
133 | break; | |
134 | } | |
135 | memmove(newname, *argv, len); | |
136 | newname[len] = '.'; | |
137 | newname[len + 1] = 'Z'; | |
138 | newname[len + 2] = '\0'; | |
139 | decompress(newname, | |
140 | cat ? "/dev/stdout" : *argv, bits); | |
141 | } else if (strcmp(p, ".Z")) { | |
142 | err(0, "%s: name missing trailing .Z", *argv); | |
143 | break; | |
144 | } else { | |
145 | if (len - 2 > sizeof(newname) - 1) { | |
146 | err(0, "%s: name too long", *argv); | |
147 | break; | |
148 | } | |
149 | memmove(newname, *argv, len - 2); | |
150 | newname[len - 2] = '\0'; | |
151 | decompress(*argv, | |
152 | cat ? "/dev/stdout" : newname, bits); | |
153 | } | |
154 | break; | |
64d06f2f | 155 | } |
5b35086d | 156 | exit (eval); |
64d06f2f KM |
157 | } |
158 | ||
5b35086d KB |
159 | void |
160 | compress(in, out, bits) | |
161 | char *in, *out; | |
162 | int bits; | |
090e3718 | 163 | { |
5b35086d KB |
164 | register int nr; |
165 | struct stat sb; | |
166 | struct timeval times[2]; | |
167 | FILE *ifp, *ofp; | |
168 | quad_t origsize; | |
3a6d9f0d | 169 | int exists, isreg, oreg; |
5b35086d KB |
170 | u_char buf[1024]; |
171 | ||
172 | exists = !stat(out, &sb); | |
173 | if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) | |
174 | return; | |
3a6d9f0d | 175 | isreg = oreg = !exists || S_ISREG(sb.st_mode); |
5b35086d KB |
176 | |
177 | ifp = ofp = NULL; | |
178 | if ((ifp = fopen(in, "r")) == NULL) { | |
179 | err(0, "%s: %s", in, strerror(errno)); | |
180 | return; | |
64d06f2f | 181 | } |
5b35086d KB |
182 | if (stat(in, &sb)) { /* DON'T FSTAT! */ |
183 | err(0, "%s: %s", in, strerror(errno)); | |
184 | goto err; | |
64d06f2f | 185 | } |
3a6d9f0d KB |
186 | if (!S_ISREG(sb.st_mode)) |
187 | isreg = 0; | |
188 | if (isreg) { | |
5b35086d KB |
189 | origsize = sb.st_size; |
190 | times[0].tv_sec = sb.st_atimespec.ts_sec; | |
191 | times[0].tv_usec = sb.st_atimespec.ts_nsec / 1000; | |
192 | times[1].tv_sec = sb.st_mtimespec.ts_sec; | |
193 | times[1].tv_usec = sb.st_mtimespec.ts_nsec / 1000; | |
64d06f2f | 194 | } |
64d06f2f | 195 | |
5b35086d KB |
196 | if ((ofp = zopen(out, "w", bits)) == NULL) { |
197 | err(0, "%s: %s", out, strerror(errno)); | |
198 | goto err; | |
090e3718 | 199 | } |
5b35086d KB |
200 | while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) |
201 | if (fwrite(buf, 1, nr, ofp) != nr) { | |
202 | err(0, "%s: %s", out, strerror(errno)); | |
203 | goto err; | |
204 | } | |
64d06f2f | 205 | |
5b35086d KB |
206 | if (ferror(ifp) || fclose(ifp)) { |
207 | err(0, "%s: %s", in, strerror(errno)); | |
208 | goto err; | |
64d06f2f | 209 | } |
5b35086d | 210 | ifp = NULL; |
64d06f2f | 211 | |
5b35086d KB |
212 | if (fclose(ofp)) { |
213 | err(0, "%s: %s", out, strerror(errno)); | |
214 | goto err; | |
64d06f2f | 215 | } |
5b35086d | 216 | ofp = NULL; |
64d06f2f | 217 | |
3a6d9f0d | 218 | if (isreg) { |
5b35086d KB |
219 | if (stat(out, &sb)) { |
220 | err(0, "%s: %s", out, strerror(errno)); | |
221 | goto err; | |
222 | } | |
3a6d9f0d KB |
223 | |
224 | if (!force && sb.st_size >= origsize) { | |
5b35086d KB |
225 | if (verbose) |
226 | (void)printf("%s: file would grow; left unmodified\n", in); | |
227 | if (unlink(out)) | |
228 | err(0, "%s: %s", out, strerror(errno)); | |
229 | goto err; | |
230 | } | |
64d06f2f | 231 | |
3a6d9f0d KB |
232 | if (unlink(in)) |
233 | err(0, "%s: %s", in, strerror(errno)); | |
64d06f2f | 234 | |
3a6d9f0d KB |
235 | if (utimes(out, times)) |
236 | err(0, "%s: %s", in, strerror(errno)); | |
64d06f2f | 237 | |
3a6d9f0d KB |
238 | if (verbose) { |
239 | (void)printf("%s: ", out); | |
240 | if (origsize > sb.st_size) | |
241 | (void)printf("%.0f%% compression\n", | |
242 | ((float)sb.st_size / origsize) * 100.0); | |
243 | else | |
244 | (void)printf("%.0f%% expansion\n", | |
245 | ((float)origsize / sb.st_size) * 100.0); | |
246 | } | |
64d06f2f | 247 | } |
5b35086d | 248 | return; |
64d06f2f | 249 | |
5b35086d KB |
250 | err: if (ofp) { |
251 | if (oreg) | |
252 | (void)unlink(out); | |
253 | (void)fclose(ofp); | |
254 | } | |
255 | if (ifp) | |
256 | (void)fclose(ifp); | |
64d06f2f KM |
257 | } |
258 | ||
5b35086d KB |
259 | void |
260 | decompress(in, out, bits) | |
261 | char *in, *out; | |
262 | int bits; | |
64d06f2f | 263 | { |
5b35086d KB |
264 | register int nr; |
265 | struct stat sb; | |
266 | struct timeval times[2]; | |
267 | FILE *ifp, *ofp; | |
3a6d9f0d | 268 | int isreg, oreg; |
5b35086d KB |
269 | u_char buf[1024]; |
270 | ||
3a6d9f0d KB |
271 | isreg = oreg = !stat(out, &sb) && S_ISREG(sb.st_mode); |
272 | if (!force && isreg && !permission(out)) | |
5b35086d KB |
273 | return; |
274 | ||
275 | ifp = ofp = NULL; | |
276 | if ((ofp = fopen(out, "w")) == NULL) { | |
277 | err(0, "%s: %s", out, strerror(errno)); | |
278 | return; | |
279 | } | |
64d06f2f | 280 | |
5b35086d KB |
281 | if ((ifp = zopen(in, "r", bits)) == NULL) { |
282 | err(0, "%s: %s", in, strerror(errno)); | |
283 | goto err; | |
64d06f2f | 284 | } |
5b35086d KB |
285 | if (stat(in, &sb)) { |
286 | err(0, "%s: %s", in, strerror(errno)); | |
287 | goto err; | |
64d06f2f | 288 | } |
3a6d9f0d KB |
289 | if (!S_ISREG(sb.st_mode)) |
290 | isreg = 0; | |
291 | if (isreg) { | |
5b35086d KB |
292 | times[0].tv_sec = sb.st_atimespec.ts_sec; |
293 | times[0].tv_usec = sb.st_atimespec.ts_nsec / 1000; | |
294 | times[1].tv_sec = sb.st_mtimespec.ts_sec; | |
295 | times[1].tv_usec = sb.st_mtimespec.ts_nsec / 1000; | |
64d06f2f | 296 | } |
5b35086d KB |
297 | |
298 | while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) | |
299 | if (fwrite(buf, 1, nr, ofp) != nr) { | |
300 | err(0, "%s: %s", out, strerror(errno)); | |
301 | goto err; | |
4eeb10d4 | 302 | } |
4eeb10d4 | 303 | |
5b35086d KB |
304 | if (ferror(ifp) || fclose(ifp)) { |
305 | err(0, "%s: %s", in, strerror(errno)); | |
306 | goto err; | |
307 | } | |
308 | ifp = NULL; | |
64d06f2f | 309 | |
5b35086d KB |
310 | if (fclose(ofp)) { |
311 | err(0, "%s: %s", out, strerror(errno)); | |
312 | goto err; | |
4eeb10d4 | 313 | } |
64d06f2f | 314 | |
3a6d9f0d KB |
315 | if (isreg) { |
316 | if (unlink(in)) | |
317 | err(0, "%s: %s", in, strerror(errno)); | |
318 | if (utimes(out, times)) | |
319 | err(0, "%s: %s", in, strerror(errno)); | |
320 | } | |
64d06f2f | 321 | return; |
64d06f2f | 322 | |
5b35086d KB |
323 | err: if (ofp) { |
324 | if (oreg) | |
325 | (void)unlink(out); | |
326 | (void)fclose(ofp); | |
327 | } | |
328 | if (ifp) | |
329 | (void)fclose(ifp); | |
64d06f2f | 330 | } |
64d06f2f | 331 | |
5b35086d KB |
332 | int |
333 | permission(fname) | |
334 | char *fname; | |
64d06f2f | 335 | { |
5b35086d KB |
336 | int ch, first; |
337 | ||
338 | if (!isatty(fileno(stderr))) | |
339 | return (0); | |
340 | (void)fprintf(stderr, "overwrite %s? ", fname); | |
341 | first = ch = getchar(); | |
342 | while (ch != '\n' && ch != EOF) | |
343 | ch = getchar(); | |
344 | return (first == 'y'); | |
4eeb10d4 JL |
345 | } |
346 | ||
392fe950 | 347 | void |
5b35086d KB |
348 | usage(iscompress) |
349 | int iscompress; | |
4eeb10d4 | 350 | { |
5b35086d KB |
351 | if (iscompress) |
352 | (void)fprintf(stderr, | |
353 | "usage: compress [-cfv] [-b bits] [file ...]\n"); | |
354 | else | |
355 | (void)fprintf(stderr, | |
356 | "usage: uncompress [-c] [-b bits] [file ...]\n"); | |
357 | exit(1); | |
64d06f2f KM |
358 | } |
359 | ||
5b35086d KB |
360 | #if __STDC__ |
361 | #include <stdarg.h> | |
362 | #else | |
363 | #include <varargs.h> | |
4eeb10d4 | 364 | #endif |
64d06f2f | 365 | |
5b35086d KB |
366 | void |
367 | #if __STDC__ | |
368 | err(int fatal, const char *fmt, ...) | |
369 | #else | |
370 | err(fatal, fmt, va_alist) | |
371 | int fatal; | |
372 | char *fmt; | |
373 | va_dcl | |
374 | #endif | |
e5f559d4 | 375 | { |
5b35086d KB |
376 | va_list ap; |
377 | #if __STDC__ | |
378 | va_start(ap, fmt); | |
e5f559d4 | 379 | #else |
5b35086d | 380 | va_start(ap); |
e5f559d4 | 381 | #endif |
5b35086d KB |
382 | (void)fprintf(stderr, "%s: ", progname); |
383 | (void)vfprintf(stderr, fmt, ap); | |
384 | va_end(ap); | |
385 | (void)fprintf(stderr, "\n"); | |
386 | if (fatal) | |
387 | exit(1); | |
388 | eval = 1; | |
e5f559d4 | 389 | } |