use <sys/time.h> macros to convert timespec to timeval
[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
3a6d9f0d 15static 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
28void compress __P((char *, char *, int));
29void decompress __P((char *, char *, int));
30void err __P((int, const char *, ...));
31int permission __P((char *));
32void usage __P((int));
64d06f2f 33
5b35086d
KB
34int eval, force, verbose;
35char *progname;
4eeb10d4 36
5b35086d 37int
e5f559d4
KB
38main(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
159void
160compress(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
250err: 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
259void
260decompress(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
323err: 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
332int
333permission(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 347void
5b35086d
KB
348usage(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
366void
367#if __STDC__
368err(int fatal, const char *fmt, ...)
369#else
370err(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}