Annotation of src/usr.bin/compress/main.c, Revision 1.32
1.32 ! mickey 1: /* $OpenBSD: main.c,v 1.31 2003/06/30 03:42:05 millert Exp $ */
1.1 mickey 2:
1.18 mickey 3: static const char copyright[] =
4: "@(#) Copyright (c) 1992, 1993\n\
5: The Regents of the University of California. All rights reserved.\n"
6: "Copyright (c) 1997-2002 Michael Shalayeff\n";
1.1 mickey 7:
1.18 mickey 8: static const char license[] =
9: "\n"
10: " Redistribution and use in source and binary forms, with or without\n"
11: " modification, are permitted provided that the following conditions\n"
12: " are met:\n"
13: " 1. Redistributions of source code must retain the above copyright\n"
14: " notice, this list of conditions and the following disclaimer.\n"
15: " 2. Redistributions in binary form must reproduce the above copyright\n"
16: " notice, this list of conditions and the following disclaimer in the\n"
17: " documentation and/or other materials provided with the distribution.\n"
1.23 deraadt 18: " 3. Neither the name of the University nor the names of its contributors\n"
19: " may be used to endorse or promote products derived from this software\n"
20: " without specific prior written permission.\n"
1.18 mickey 21: "\n"
22: " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
23: " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
24: " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
25: " IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,\n"
26: " INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"
27: " (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
28: " SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
29: " HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
30: " STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
31: " IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
32: " THE POSSIBILITY OF SUCH DAMAGE.\n";
1.1 mickey 33:
34: #ifndef lint
35: #if 0
36: static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
37: #else
1.32 ! mickey 38: static const char main_rcsid[] = "$OpenBSD: main.c,v 1.31 2003/06/30 03:42:05 millert Exp $";
1.1 mickey 39: #endif
40: #endif /* not lint */
41:
42: #include <sys/param.h>
43: #include <sys/time.h>
44: #include <sys/stat.h>
45:
1.18 mickey 46: #include <getopt.h>
1.1 mickey 47: #include <err.h>
48: #include <errno.h>
1.19 millert 49: #include <fts.h>
1.1 mickey 50: #include <stdio.h>
51: #include <stdlib.h>
52: #include <string.h>
53: #include <unistd.h>
54: #include <fcntl.h>
55: #include <paths.h>
56: #include "compress.h"
57:
58: #define min(a,b) ((a) < (b)? (a) : (b))
59:
1.18 mickey 60: int pipin, force, verbose, testmode, list, nosave;
61: int savename, recurse;
62: int bits, cat, decomp;
1.1 mickey 63: extern char *__progname;
64:
1.18 mickey 65: const struct compressor {
1.1 mickey 66: char *name;
67: char *suffix;
1.17 millert 68: int (*check_header)(int, struct stat *, const char *);
69: void *(*open)(int, const char *, int);
70: int (*read)(void *, char *, int);
71: int (*write)(void *, const char *, int);
72: int (*close)(void *);
1.1 mickey 73: } c_table[] = {
74: #define M_COMPRESS (&c_table[0])
75: { "compress", ".Z", z_check_header, z_open, zread, zwrite, zclose },
76: #define M_DEFLATE (&c_table[1])
77: { "deflate", ".gz", gz_check_header, gz_open, gz_read, gz_write, gz_close },
1.18 mickey 78: #if 0
79: #define M_LZH (&c_table[2])
80: { "lzh", ".lzh", lzh_check_header, lzh_open, lzh_read, lzh_write, lzh_close },
81: #define M_ZIP (&c_table[3])
82: { "zip", ".zip", zip_check_header, zip_open, zip_read, zip_write, zip_close },
83: #define M_PACK (&c_table[4])
84: { "pack", ".pak",pak_check_header, pak_open, pak_read, pak_write, pak_close },
85: #endif
1.1 mickey 86: { NULL }
87: };
88:
1.18 mickey 89: int permission(const char *);
90: void setfile(const char *, struct stat *);
1.31 millert 91: __dead void usage(int);
1.25 deraadt 92: int compress(const char *, const char *, const struct compressor *,
93: int, struct stat *);
94: int decompress(const char *, const char *, const struct compressor *,
95: int, struct stat *);
1.18 mickey 96: const struct compressor *check_method(int, struct stat *, const char *);
97:
1.19 millert 98: #define OPTSTRING "123456789ab:cdfghlLnNOo:qrS:tvV"
1.18 mickey 99: const struct option longopts[] = {
100: { "ascii", no_argument, 0, 'a' },
101: { "stdout", no_argument, 0, 'c' },
102: { "to-stdout", no_argument, 0, 'c' },
103: { "decompress", no_argument, 0, 'd' },
104: { "uncompress", no_argument, 0, 'd' },
105: { "force", no_argument, 0, 'f' },
106: { "help", no_argument, 0, 'h' },
107: { "list", no_argument, 0, 'l' },
108: { "license", no_argument, 0, 'L' },
109: { "no-name", no_argument, 0, 'n' },
110: { "name", no_argument, 0, 'N' },
111: { "quiet", no_argument, 0, 'q' },
112: { "recursive", no_argument, 0, 'r' },
113: { "suffix", required_argument, 0, 'S' },
114: { "test", no_argument, 0, 't' },
115: { "verbose", no_argument, 0, 'v' },
116: { "version", no_argument, 0, 'V' },
117: { "fast", no_argument, 0, '1' },
118: { "best", no_argument, 0, '9' },
119: { NULL }
120: };
1.1 mickey 121:
122: int
1.24 deraadt 123: main(int argc, char *argv[])
1.1 mickey 124: {
1.19 millert 125: FTS *ftsp;
126: FTSENT *entry;
127: struct stat osb;
1.18 mickey 128: const struct compressor *method;
1.19 millert 129: char *p, *s, *infile;
130: char outfile[MAXPATHLEN], _infile[MAXPATHLEN], suffix[16];
1.18 mickey 131: char *nargv[512]; /* some estimate based on ARG_MAX */
1.19 millert 132: int exists, oreg, ch, error, i, rc, oflag;
1.1 mickey 133:
1.19 millert 134: bits = cat = oflag = decomp = 0;
1.1 mickey 135: p = __progname;
136: if (p[0] == 'g') {
137: method = M_DEFLATE;
1.18 mickey 138: bits = 6;
1.1 mickey 139: p++;
140: } else
141: method = M_COMPRESS;
142:
143: decomp = 0;
144: if (!strcmp(p, "zcat")) {
145: decomp++;
1.20 mickey 146: cat = 1;
1.1 mickey 147: } else {
148: if (p[0] == 'u' && p[1] == 'n') {
149: p += 2;
150: decomp++;
151: }
152:
1.2 mickey 153: if (strcmp(p, "zip") &&
154: strcmp(p, "compress"))
1.1 mickey 155: errx(1, "unknown program name");
156: }
157:
1.18 mickey 158: strlcpy(suffix, method->suffix, sizeof(suffix));
159:
1.19 millert 160: nargv[0] = NULL;
1.18 mickey 161: if ((s = getenv("GZIP")) != NULL) {
162: char *last;
163:
164: nargv[0] = *argv++;
165: for (i = 1, (p = strtok_r(s, " ", &last)); p;
166: (p = strtok_r(NULL, " ", &last)), i++)
167: if (i < sizeof(nargv)/sizeof(nargv[1]) - argc - 1)
168: nargv[i] = p;
169: else {
170: errx(1, "GZIP is too long");
171: }
172: argc += i - 1;
173: while ((nargv[i++] = *argv++))
174: ;
175: argv = nargv;
176: }
177:
178: while ((ch = getopt_long(argc, argv, OPTSTRING, longopts, NULL)) != -1)
1.1 mickey 179: switch(ch) {
180: case '1':
181: case '2':
182: case '3':
183: case '4':
184: case '5':
185: case '6':
186: case '7':
187: case '8':
188: case '9':
189: method = M_DEFLATE;
1.18 mickey 190: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 191: bits = ch - '0';
192: break;
1.18 mickey 193: case 'a':
194: warnx("option -a is ignored on this system");
195: break;
1.1 mickey 196: case 'b':
197: bits = strtol(optarg, &p, 10);
1.7 denny 198: /*
199: * POSIX 1002.3 says 9 <= bits <= 14 for portable
200: * apps, but says the implementation may allow
201: * greater.
202: */
1.1 mickey 203: if (*p)
204: errx(1, "illegal bit count -- %s", optarg);
205: break;
206: case 'c':
1.20 mickey 207: cat = 1;
1.1 mickey 208: break;
209: case 'd': /* Backward compatible. */
210: decomp++;
211: break;
212: case 'f':
213: force++;
214: break;
215: case 'g':
216: method = M_DEFLATE;
1.18 mickey 217: strlcpy(suffix, method->suffix, sizeof(suffix));
218: bits = 6;
1.1 mickey 219: break;
220: case 'l':
221: list++;
222: break;
223: case 'n':
224: nosave++;
225: break;
226: case 'N':
1.18 mickey 227: nosave = 0; /* XXX not yet */
1.1 mickey 228: break;
229: case 'O':
230: method = M_COMPRESS;
1.18 mickey 231: strlcpy(suffix, method->suffix, sizeof(suffix));
1.1 mickey 232: break;
233: case 'o':
1.18 mickey 234: if (strlcpy(outfile, optarg,
235: sizeof(outfile)) >= sizeof(outfile))
236: errx(1, "-o argument is too long");
1.20 mickey 237: oflag = 1;
1.1 mickey 238: break;
239: case 'q':
240: verbose = -1;
241: break;
242: case 'S':
243: p = suffix;
244: if (optarg[0] != '.')
245: *p++ = '.';
1.18 mickey 246: strlcpy(p, optarg, sizeof(suffix) - (p - suffix));
247: p = optarg;
1.1 mickey 248: break;
249: case 't':
1.20 mickey 250: testmode = 1;
1.29 millert 251: decomp++;
1.1 mickey 252: break;
1.18 mickey 253: case 'V':
254: printf("%s\n%s\n%s\n", main_rcsid,
255: z_rcsid, gz_rcsid);
1.19 millert 256: exit (0);
1.1 mickey 257: case 'v':
258: verbose++;
259: break;
1.18 mickey 260: case 'L':
261: fputs(copyright, stderr);
262: fputs(license, stderr);
1.19 millert 263: exit (0);
1.18 mickey 264: case 'r':
1.19 millert 265: recurse++;
1.18 mickey 266: break;
267:
1.1 mickey 268: case 'h':
1.31 millert 269: usage(0);
270: break;
1.1 mickey 271: default:
1.31 millert 272: usage(1);
1.1 mickey 273: }
274: argc -= optind;
275: argv += optind;
276:
1.19 millert 277: if (argc == 0) {
278: if (nargv[0] == NULL)
279: argv = nargv;
280: /* XXX - make sure we don't oflow nargv in $GZIP case (millert) */
281: argv[0] = "/dev/stdin";
282: argv[1] = NULL;
283: pipin++;
1.20 mickey 284: cat = 1;
1.28 millert 285: } else {
286: for (i = 0; i < argc; i++) {
287: if (argv[i][0] == '-' && argv[i][1] == '\0') {
288: argv[i] = "/dev/stdin";
289: pipin++;
290: cat = 1;
291: }
292: }
1.19 millert 293: }
294: if (oflag && (recurse || argc > 1))
295: errx(1, "-o option may only be used with a single input file");
1.20 mickey 296:
1.19 millert 297: if (cat + testmode + oflag > 1)
298: errx(1, "may not mix -o, -c, or -t options");
299:
300: if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL)
301: err(1, NULL);
302: /* XXX - set rc in cases where we "continue" below? */
303: for (rc = 0; (entry = fts_read(ftsp)) != NULL;) {
304: infile = entry->fts_path;
305: switch (entry->fts_info) {
306: case FTS_D:
307: if (!recurse) {
308: warnx("%s is a directory: ignored",
309: infile);
310: fts_set(ftsp, entry, FTS_SKIP);
311: }
312: continue;
313: case FTS_DP:
314: continue;
315: case FTS_NS:
316: /*
317: * If file does not exist and has no suffix,
318: * tack on the default suffix and try that.
319: */
320: /* XXX - is overwriting fts_statp legal? (millert) */
321: if (entry->fts_errno == ENOENT &&
322: strchr(entry->fts_accpath, '.') == NULL &&
323: snprintf(_infile, sizeof(_infile), "%s%s", infile,
324: suffix) < sizeof(_infile) &&
325: stat(_infile, entry->fts_statp) == 0 &&
326: S_ISREG(entry->fts_statp->st_mode)) {
327: infile = _infile;
328: break;
329: }
330: case FTS_ERR:
331: case FTS_DNR:
332: warnx("%s: %s", infile, strerror(entry->fts_errno));
333: error = 1;
334: continue;
335: default:
1.20 mickey 336: if (!S_ISREG(entry->fts_statp->st_mode) && !pipin) {
1.27 millert 337: warnx("%s not a regular file%s",
1.28 millert 338: infile, cat ? "" : ": unchanged");
1.19 millert 339: continue;
1.18 mickey 340: }
1.19 millert 341: break;
1.9 mickey 342: }
1.1 mickey 343:
344: if (testmode)
1.22 deraadt 345: strlcpy(outfile, _PATH_DEVNULL, sizeof outfile);
1.19 millert 346: else if (cat)
1.22 deraadt 347: strlcpy(outfile, "/dev/stdout", sizeof outfile);
1.19 millert 348: else if (!oflag) {
349: if (decomp) {
350: const struct compressor *m = method;
351:
352: if ((s = strrchr(infile, '.')) != NULL &&
353: strcmp(s, suffix) != 0) {
354: for (m = &c_table[0];
355: m->name && strcmp(s, m->suffix);
356: m++)
357: ;
358: }
359: if (s == NULL || m->name == NULL) {
360: if (!recurse)
361: warnx("%s: unknown suffix: "
362: "ignored", infile);
363: continue;
364: }
365: method = m;
366: strlcpy(outfile, infile,
367: min(sizeof(outfile), (s - infile) + 1));
368: } else {
369: if (snprintf(outfile, sizeof(outfile),
370: "%s%s", infile, suffix) >= sizeof(outfile)) {
371: warnx("%s%s: name too long",
372: infile, suffix);
373: continue;
374: }
375: }
1.1 mickey 376: }
377:
1.19 millert 378: exists = !stat(outfile, &osb);
379: if (!force && exists && S_ISREG(osb.st_mode) &&
380: !permission(outfile))
1.1 mickey 381: continue;
382:
1.19 millert 383: oreg = !exists || S_ISREG(osb.st_mode);
1.1 mickey 384:
385: if (verbose > 0)
386: fprintf(stderr, "%s:\t", infile);
387:
1.19 millert 388: error = (decomp ? decompress : compress)
389: (infile, outfile, method, bits, entry->fts_statp);
1.1 mickey 390:
1.19 millert 391: if (!error && !cat && !testmode && stat(outfile, &osb) == 0) {
392: if (!force && !decomp &&
393: osb.st_size >= entry->fts_statp->st_size) {
1.1 mickey 394: if (verbose > 0)
395: fprintf(stderr, "file would grow; "
1.25 deraadt 396: "left unmodified\n");
1.1 mickey 397: error = 1;
1.19 millert 398: rc = rc ? rc : 2;
1.1 mickey 399: } else {
1.19 millert 400: setfile(outfile, entry->fts_statp);
1.1 mickey 401:
402: if (unlink(infile) && verbose >= 0)
1.18 mickey 403: warn("input: %s", infile);
1.1 mickey 404:
405: if (verbose > 0) {
406: u_int ratio;
1.19 millert 407: ratio = (1000 * osb.st_size)
408: / entry->fts_statp->st_size;
1.1 mickey 409: fprintf(stderr, "%u", ratio / 10);
410: if (ratio % 10)
411: fprintf(stderr, ".%u",
1.25 deraadt 412: ratio % 10);
1.1 mickey 413: fputc('%', stderr);
414: fputc(' ', stderr);
415: }
416: }
417: }
418:
1.21 millert 419: if (error > 0 && oreg && unlink(outfile) && errno != ENOENT &&
1.18 mickey 420: verbose >= 0) {
421: if (force) {
422: warn("output: %s", outfile);
423: rc = 1;
424: } else
425: err(1, "output: %s", outfile);
426: } else if (!error && verbose > 0)
1.1 mickey 427: fputs("OK\n", stderr);
1.19 millert 428: }
1.1 mickey 429:
1.18 mickey 430: exit(rc);
1.1 mickey 431: }
432:
433: int
1.24 deraadt 434: compress(const char *in, const char *out, const struct compressor *method,
435: int bits, struct stat *sb)
1.1 mickey 436: {
1.18 mickey 437: u_char buf[Z_BUFSIZE];
438: int error, ifd, ofd;
1.16 mpech 439: void *cookie;
440: ssize_t nr;
1.1 mickey 441:
442: error = 0;
443: cookie = NULL;
1.3 mickey 444:
1.21 millert 445: if ((ifd = open(in, O_RDONLY)) < 0) {
446: if (verbose >= 0)
447: warn("%s", out);
448: return (-1);
449: }
450:
1.3 mickey 451: if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) {
452: if (verbose >= 0)
453: warn("%s", out);
1.32 ! mickey 454: (void) close(ifd);
1.19 millert 455: return (-1);
1.3 mickey 456: }
457:
1.4 mickey 458: if (method != M_COMPRESS && !force && isatty(ofd)) {
1.3 mickey 459: if (verbose >= 0)
460: warnx("%s: won't write compressed data to terminal",
1.25 deraadt 461: out);
1.32 ! mickey 462: (void) close(ofd);
! 463: (void) close(ifd);
1.19 millert 464: return (-1);
1.3 mickey 465: }
1.1 mickey 466:
1.32 ! mickey 467: if ((cookie = (*method->open)(ofd, "w", bits)) == NULL) {
! 468: if (verbose >= 0)
! 469: warn("%s", in);
! 470: (void) close(ofd);
! 471: (void) close(ifd);
! 472: return (-1);
! 473: }
1.1 mickey 474:
1.32 ! mickey 475: while ((nr = read(ifd, buf, sizeof(buf))) > 0)
! 476: if ((method->write)(cookie, buf, nr) != nr) {
! 477: if (verbose >= 0)
! 478: warn("%s", out);
! 479: error++;
! 480: break;
! 481: }
1.1 mickey 482:
1.32 ! mickey 483: if (!error && nr < 0) {
! 484: if (verbose >= 0)
1.1 mickey 485: warn("%s", in);
486: error++;
487: }
488:
1.32 ! mickey 489: if ((method->close)(cookie)) {
1.1 mickey 490: if (!error && verbose >= 0)
491: warn("%s", out);
492: error++;
493: }
494:
1.18 mickey 495: if (close(ifd)) {
496: if (!error && verbose >= 0)
1.32 ! mickey 497: warn("%s", in);
1.18 mickey 498: error++;
499: }
1.32 ! mickey 500:
1.21 millert 501: return (error);
1.1 mickey 502: }
503:
1.18 mickey 504: const struct compressor *
1.24 deraadt 505: check_method(int fd, struct stat *sb, const char *out)
1.1 mickey 506: {
1.18 mickey 507: const struct compressor *method;
1.1 mickey 508:
509: for (method = &c_table[0];
1.25 deraadt 510: method->name != NULL && !(*method->check_header)(fd, sb, out);
511: method++)
1.1 mickey 512: ;
513:
514: if (method->name == NULL)
515: method = NULL;
516:
1.19 millert 517: return (method);
1.1 mickey 518: }
519:
520: int
1.24 deraadt 521: decompress(const char *in, const char *out, const struct compressor *method,
522: int bits, struct stat *sb)
1.1 mickey 523: {
1.18 mickey 524: u_char buf[Z_BUFSIZE];
525: int error, ifd, ofd;
1.16 mpech 526: void *cookie;
527: ssize_t nr;
1.1 mickey 528:
529: error = 0;
530: cookie = NULL;
531:
1.3 mickey 532: if ((ifd = open(in, O_RDONLY)) < 0) {
533: if (verbose >= 0)
534: warn("%s", in);
535: return -1;
536: }
537:
538: if (!force && isatty(ifd)) {
539: if (verbose >= 0)
540: warnx("%s: won't read compressed data from terminal",
1.25 deraadt 541: in);
1.3 mickey 542: close (ifd);
543: return -1;
544: }
545:
1.18 mickey 546: if (!pipin && (method = check_method(ifd, sb, out)) == NULL) {
1.3 mickey 547: if (verbose >= 0)
548: warnx("%s: unrecognized file format", in);
1.13 d 549: close (ifd);
1.3 mickey 550: return -1;
551: }
552:
1.32 ! mickey 553: if ((cookie = (*method->open)(ifd, "r", bits)) == NULL) {
! 554: if (verbose >= 0)
! 555: warn("%s", in);
! 556: error++;
! 557: close (ifd);
! 558: return -1;
! 559: }
! 560:
! 561: if ((ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR)) < 0) {
! 562: if (verbose >= 0)
! 563: warn("%s", in);
! 564: (method->close)(cookie);
! 565: return -1;
! 566: }
! 567:
! 568: while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0)
! 569: if (write(ofd, buf, nr) != nr) {
1.21 millert 570: if (verbose >= 0)
1.32 ! mickey 571: warn("%s", out);
! 572: error++;
! 573: break;
1.21 millert 574: }
1.1 mickey 575:
1.32 ! mickey 576: if (!error && nr < 0) {
! 577: if (verbose >= 0)
! 578: warnx("%s: %s", in,
! 579: errno == EINVAL ? "crc error" : strerror(errno));
! 580: error++;
1.1 mickey 581: }
582:
1.32 ! mickey 583: if ((method->close)(cookie)) {
1.1 mickey 584: if (!error && verbose >= 0)
1.32 ! mickey 585: warnx("%s", in);
1.1 mickey 586: error++;
587: }
588:
1.18 mickey 589: if (close(ofd)) {
1.1 mickey 590: if (!error && verbose >= 0)
1.18 mickey 591: warn("%s", out);
1.1 mickey 592: error++;
593: }
594:
1.21 millert 595: return (error);
1.1 mickey 596: }
597:
598: void
1.24 deraadt 599: setfile(const char *name, struct stat *fs)
1.1 mickey 600: {
1.18 mickey 601: struct timeval tv[2];
1.1 mickey 602:
603: fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
604:
605: TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
606: TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
607: if (utimes(name, tv))
608: warn("utimes: %s", name);
609:
610: /*
611: * Changing the ownership probably won't succeed, unless we're root
612: * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting
613: * the mode; current BSD behavior is to remove all setuid bits on
614: * chown. If chown fails, lose setuid/setgid bits.
615: */
616: if (chown(name, fs->st_uid, fs->st_gid)) {
617: if (errno != EPERM)
618: warn("chown: %s", name);
619: fs->st_mode &= ~(S_ISUID|S_ISGID);
620: }
621: if (chmod(name, fs->st_mode))
622: warn("chown: %s", name);
623:
1.8 millert 624: if (fs->st_flags && chflags(name, fs->st_flags))
1.1 mickey 625: warn("chflags: %s", name);
626: }
627:
628: int
1.24 deraadt 629: permission(const char *fname)
1.1 mickey 630: {
631: int ch, first;
632:
633: if (!isatty(fileno(stderr)))
634: return (0);
635: (void)fprintf(stderr, "overwrite %s? ", fname);
636: first = ch = getchar();
637: while (ch != '\n' && ch != EOF)
638: ch = getchar();
639: return (first == 'y');
640: }
641:
1.31 millert 642: __dead void
643: usage(int status)
1.1 mickey 644: {
645: fprintf(stderr,
1.31 millert 646: "usage: %s [-cdfghOqrtvV] [-b bits] [-S suffix] [-[1-9]] [file ...]\n",
1.18 mickey 647: __progname);
1.31 millert 648: exit(status);
1.1 mickey 649: }