[BACK]Return to main.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / compress

Annotation of src/usr.bin/compress/main.c, Revision 1.14

1.14    ! millert     1: /*     $OpenBSD: main.c,v 1.13 2000/03/10 06:53:51 d Exp $     */
1.1       mickey      2:
                      3: /*-
                      4:  * Copyright (c) 1992, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  * 3. All advertising materials mentioning features or use of this software
                     16:  *    must display the following acknowledgement:
                     17:  *     This product includes software developed by the University of
                     18:  *     California, Berkeley and its contributors.
                     19:  * 4. Neither the name of the University nor the names of its contributors
                     20:  *    may be used to endorse or promote products derived from this software
                     21:  *    without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     33:  * SUCH DAMAGE.
                     34:  */
                     35:
                     36: static char copyright[] =
                     37: "@(#) Copyright (c) 1992, 1993\n\
                     38:        The Regents of the University of California.  All rights reserved.\n";
                     39:
                     40: #ifndef lint
                     41: #if 0
                     42: static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94";
                     43: #else
1.14    ! millert    44: static char rcsid[] = "$OpenBSD: main.c,v 1.13 2000/03/10 06:53:51 d Exp $";
1.1       mickey     45: #endif
                     46: #endif /* not lint */
                     47:
                     48: #include <sys/param.h>
                     49: #include <sys/time.h>
                     50: #include <sys/stat.h>
                     51:
                     52: #include <err.h>
                     53: #include <errno.h>
                     54: #include <stdio.h>
                     55: #include <stdlib.h>
                     56: #include <string.h>
                     57: #include <unistd.h>
                     58: #include <fcntl.h>
                     59: #include <paths.h>
                     60: #include "compress.h"
                     61:
                     62: #define min(a,b) ((a) < (b)? (a) : (b))
                     63:
1.9       mickey     64: int pipin = 0, force = 0, verbose = 0, testmode = 0, list = 0, nosave = 0;
1.1       mickey     65: extern char *__progname;
                     66:
                     67: struct compressor {
                     68:        char *name;
                     69:        char *suffix;
                     70:        int (*check_header) __P((int, struct stat *, const char *));
                     71:        void *(*open) __P((int, const char *, int));
                     72:        int (*read) __P((void *, char *, int));
                     73:        int (*write) __P((void *, const char *, int));
                     74:        int (*close) __P((void *));
                     75: } c_table[] = {
                     76: #define M_COMPRESS (&c_table[0])
                     77:   { "compress", ".Z", z_check_header,  z_open,  zread,   zwrite,   zclose },
                     78: #define M_DEFLATE (&c_table[1])
                     79:   { "deflate", ".gz", gz_check_header, gz_open, gz_read, gz_write, gz_close },
                     80:   { NULL }
                     81: };
                     82:
                     83: int permission __P((char *));
                     84: void setfile __P((char *, struct stat *));
                     85: void usage __P((void));
                     86: int compress
                     87:        __P((const char *, const char *, register struct compressor *, int));
                     88: int decompress
                     89:        __P((const char *, const char *, register struct compressor *, int));
                     90: struct compressor *check_method __P((int, const char *));
                     91:
                     92: struct stat sb, osb;
                     93:
                     94: int
                     95: main(argc, argv)
                     96:        int argc;
                     97:        char *argv[];
                     98: {
                     99:        int ch, bits, cat, decomp, error;
                    100:        struct compressor *method;
                    101:        int exists, isreg, oreg;
1.7       denny     102:        char *infile, outfile[MAXPATHLEN+4], suffix[16];
1.1       mickey    103:        char *p;
1.7       denny     104:        int rc = 0;
1.1       mickey    105:
1.6       mickey    106:        bits = cat = decomp = 0;
1.1       mickey    107:        p = __progname;
                    108:        if (p[0] == 'g') {
                    109:                method = M_DEFLATE;
                    110:                p++;
                    111:        } else
                    112:                method = M_COMPRESS;
                    113:
                    114:        decomp = 0;
                    115:        if (!strcmp(p, "zcat")) {
                    116:                decomp++;
                    117:                cat++;
                    118:        } else {
                    119:                if (p[0] == 'u' && p[1] == 'n') {
                    120:                        p += 2;
                    121:                        decomp++;
                    122:                }
                    123:
1.2       mickey    124:                if (strcmp(p, "zip") &&
                    125:                    strcmp(p, "compress"))
1.1       mickey    126:                        errx(1, "unknown program name");
                    127:        }
                    128:
                    129:        outfile[0] = '\0';
                    130:        while ((ch = getopt(argc, argv, "0123456789b:cdfghlnOo:qStv")) != -1)
                    131:                switch(ch) {
                    132:                case '0':
                    133:                case '1':
                    134:                case '2':
                    135:                case '3':
                    136:                case '4':
                    137:                case '5':
                    138:                case '6':
                    139:                case '7':
                    140:                case '8':
                    141:                case '9':
                    142:                        method = M_DEFLATE;
                    143:                        bits = ch - '0';
                    144:                        break;
                    145:                case 'b':
                    146:                        bits = strtol(optarg, &p, 10);
1.7       denny     147:                        /*
                    148:                         * POSIX 1002.3 says 9 <= bits <= 14 for portable
                    149:                         * apps, but says the implementation may allow
                    150:                         * greater.
                    151:                         */
1.1       mickey    152:                        if (*p)
                    153:                                errx(1, "illegal bit count -- %s", optarg);
                    154:                        break;
                    155:                case 'c':
                    156:                        cat++;
                    157:                        break;
                    158:                case 'd':               /* Backward compatible. */
                    159:                        decomp++;
                    160:                        break;
                    161:                case 'f':
                    162:                        force++;
                    163:                        break;
                    164:                case 'g':
                    165:                        method = M_DEFLATE;
                    166:                        break;
                    167:                case 'l':
                    168:                        list++;
                    169:                        break;
                    170:                case 'L':
                    171:                        fputs(copyright, stderr);
                    172:                case 'n':
                    173:                        nosave++;
                    174:                        break;
                    175:                case 'N':
                    176:                        nosave = 0;
                    177:                        break;
                    178:                case 'O':
                    179:                        method = M_COMPRESS;
                    180:                        break;
                    181:                case 'o':
                    182:                        strncpy(outfile, optarg, sizeof(outfile)-1);
1.10      deraadt   183:                        outfile[sizeof(outfile)-1] = '\0';
1.1       mickey    184:                        break;
                    185:                case 'q':
                    186:                        verbose = -1;
                    187:                        break;
                    188:                case 'S':
                    189:                        p = suffix;
                    190:                        if (optarg[0] != '.')
                    191:                                *p++ = '.';
                    192:                        strncpy(p, optarg, sizeof(suffix) - (p - suffix) - 1);
                    193:                        break;
                    194:                case 't':
                    195:                        testmode++;
                    196:                        break;
                    197:                case 'v':
                    198:                        verbose++;
                    199:                        break;
                    200:                case 'h':
                    201:                case '?':
                    202:                default:
                    203:                        usage();
                    204:                }
                    205:        argc -= optind;
                    206:        argv += optind;
                    207:
                    208:        do {
                    209:                if (*argv != NULL) {
                    210:                        infile = *argv;
                    211:                        if (outfile[0] == '\0') {
1.7       denny     212:                                if (!decomp && !cat && outfile[0] == '\0') {
                    213:                                        int len;
                    214:                                        char *p;
                    215:
                    216:                                        snprintf(outfile, sizeof(outfile),
                    217:                                                "%s%s", infile,
                    218:                                                method->suffix);
                    219:
                    220:                                        len = strlen(outfile);
                    221:                                        if (len > MAXPATHLEN) {
                    222:                                                errx(1, "pathname%s too long",
                    223:                                                        method->suffix);
                    224:                                        }
                    225:
                    226:                                        p = strrchr(outfile, '/');
                    227:                                        if (p == NULL) p = outfile;
                    228:                                        len = strlen(p);
                    229:                                        if (len > NAME_MAX) {
                    230:                                                errx(1, "filename%s too long",
                    231:                                                        method->suffix);
                    232:                                        }
                    233:                                } else if (decomp && !cat) {
1.1       mickey    234:                                        char *p = strrchr(infile, '.');
                    235:                                        if (p != NULL)
                    236:                                                for (method = &c_table[0];
                    237:                                                     method->name != NULL &&
                    238:                                                        !strcmp(p, method->suffix);
                    239:                                                     method++)
                    240:                                                        ;
                    241:                                        if (method->name != NULL) {
                    242:                                                int l = min(sizeof(outfile),
                    243:                                                            (p - infile));
                    244:                                                strncpy(outfile, infile, l);
                    245:                                                outfile[l] = '\0';
                    246:                                        }
                    247:                                }
                    248:                        }
1.9       mickey    249:                } else {
1.1       mickey    250:                        infile = "/dev/stdin";
1.9       mickey    251:                        pipin++;
                    252:                }
1.1       mickey    253:
                    254:                if (testmode)
                    255:                        strcpy(outfile, _PATH_DEVNULL);
                    256:                else if (cat || outfile[0] == '\0') {
                    257:                        strcpy(outfile, "/dev/stdout");
                    258:                        cat++;
                    259:                }
                    260:
                    261:                exists = !stat(outfile, &sb);
                    262:                if (!force && exists && S_ISREG(sb.st_mode) &&
                    263:                    !permission(outfile)) {
                    264:                        argv++;
                    265:                        continue;
                    266:                }
                    267:                isreg = oreg = !exists || S_ISREG(sb.st_mode);
                    268:
                    269:                if (stat(infile, &sb) != 0 && verbose >= 0)
1.14    ! millert   270:                        err(1, "%s", infile);
1.1       mickey    271:
                    272:                if (!S_ISREG(sb.st_mode))
                    273:                        isreg = 0;
                    274:
                    275:                if (verbose > 0)
                    276:                        fprintf(stderr, "%s:\t", infile);
                    277:
                    278:                error = (decomp? decompress: compress)
                    279:                        (infile, outfile, method, bits);
                    280:
                    281:                if (!error && isreg && stat(outfile, &osb) == 0) {
                    282:
                    283:                        if (!force && !decomp && osb.st_size >= sb.st_size) {
                    284:                                if (verbose > 0)
                    285:                                        fprintf(stderr, "file would grow; "
                    286:                                                     "left unmodified\n");
                    287:                                error = 1;
1.7       denny     288:                                rc = 2;
1.1       mickey    289:                        } else {
                    290:
                    291:                                setfile(outfile, &sb);
                    292:
                    293:                                if (unlink(infile) && verbose >= 0)
                    294:                                        warn("%s", infile);
                    295:
                    296:                                if (verbose > 0) {
                    297:                                        u_int ratio;
                    298:                                        ratio = (1000*osb.st_size)/sb.st_size;
                    299:                                        fprintf(stderr, "%u", ratio / 10);
                    300:                                        if (ratio % 10)
                    301:                                                fprintf(stderr, ".%u",
                    302:                                                        ratio % 10);
                    303:                                        fputc('%', stderr);
                    304:                                        fputc(' ', stderr);
                    305:                                }
                    306:                        }
                    307:                }
                    308:
1.5       mickey    309:                if (error && oreg && unlink(outfile) && errno != ENOENT &&
                    310:                    verbose >= 0)
1.1       mickey    311:                        warn("%s", outfile);
                    312:                else if (!error && verbose > 0)
                    313:                        fputs("OK\n", stderr);
                    314:
                    315:                outfile[0] = '\0';
                    316:                if (*argv != NULL)
                    317:                        argv++;
                    318:
                    319:        } while (*argv != NULL);
                    320:
1.7       denny     321:        return (rc);
1.1       mickey    322: }
                    323:
                    324: int
                    325: compress(in, out, method, bits)
                    326:        const char *in;
                    327:        const char *out;
                    328:        register struct compressor *method;
                    329:        int bits;
                    330: {
                    331:        register int ifd;
                    332:        int ofd;
                    333:        register void *cookie;
1.11      espie     334:        register ssize_t nr;
1.1       mickey    335:        u_char buf[Z_BUFSIZE];
                    336:        int error;
                    337:
                    338:        error = 0;
                    339:        cookie  = NULL;
1.3       mickey    340:
                    341:        if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) {
                    342:                if (verbose >= 0)
                    343:                        warn("%s", out);
                    344:                return -1;
                    345:        }
                    346:
1.4       mickey    347:        if (method != M_COMPRESS && !force && isatty(ofd)) {
1.3       mickey    348:                if (verbose >= 0)
                    349:                        warnx("%s: won't write compressed data to terminal",
                    350:                              out);
                    351:                return -1;
                    352:        }
1.1       mickey    353:
                    354:        if ((ifd = open(in, O_RDONLY)) >= 0 &&
                    355:            (cookie = (*method->open)(ofd, "w", bits)) != NULL) {
                    356:
1.12      mickey    357:                while ((nr = read(ifd, buf, sizeof(buf))) > 0)
1.1       mickey    358:                        if ((method->write)(cookie, buf, nr) != nr) {
                    359:                                if (verbose >= 0)
                    360:                                        warn("%s", out);
                    361:                                error++;
                    362:                                break;
                    363:                        }
                    364:        }
                    365:
1.12      mickey    366:        if (ifd < 0 || close(ifd) || nr < 0) {
1.1       mickey    367:                if (!error && verbose >= 0)
                    368:                        warn("%s", in);
                    369:                error++;
                    370:        }
                    371:
1.3       mickey    372:        if (cookie == NULL || (method->close)(cookie)) {
1.1       mickey    373:                if (!error && verbose >= 0)
                    374:                        warn("%s", out);
                    375:                error++;
                    376:                (void) close(ofd);
                    377:        }
                    378:
                    379:        return error? -1 : 0;
                    380: }
                    381:
                    382: struct compressor *
                    383: check_method(fd, out)
                    384:        int fd;
                    385:        const char *out;
                    386: {
                    387:        register struct compressor *method;
                    388:
                    389:        for (method = &c_table[0];
                    390:             method->name != NULL &&
                    391:                     !(*method->check_header)(fd, &sb, out);
                    392:             method++)
                    393:                ;
                    394:
                    395:        if (method->name == NULL)
                    396:                method = NULL;
                    397:
                    398:        return method;
                    399: }
                    400:
                    401: int
                    402: decompress(in, out, method, bits)
                    403:        const char *in;
                    404:        const char *out;
                    405:        register struct compressor *method;
                    406:        int bits;
                    407: {
                    408:        int ifd;
                    409:        register int ofd;
                    410:        register void *cookie;
1.11      espie     411:        register ssize_t nr;
1.1       mickey    412:        u_char buf[Z_BUFSIZE];
                    413:        int error;
                    414:
                    415:        error = 0;
                    416:        cookie = NULL;
                    417:
1.3       mickey    418:        if ((ifd = open(in, O_RDONLY)) < 0) {
                    419:                if (verbose >= 0)
                    420:                        warn("%s", in);
                    421:                return -1;
                    422:        }
                    423:
                    424:        if (!force && isatty(ifd)) {
                    425:                if (verbose >= 0)
                    426:                        warnx("%s: won't read compressed data from terminal",
                    427:                              in);
                    428:                close (ifd);
                    429:                return -1;
                    430:        }
                    431:
1.9       mickey    432:        if (!pipin && (method = check_method(ifd, out)) == NULL) {
1.3       mickey    433:                if (verbose >= 0)
                    434:                        warnx("%s: unrecognized file format", in);
1.13      d         435:                close (ifd);
1.3       mickey    436:                return -1;
                    437:        }
                    438:
1.1       mickey    439:        if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) >= 0 &&
                    440:            (cookie = (*method->open)(ifd, "r", bits)) != NULL) {
                    441:
1.12      mickey    442:                while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0)
1.1       mickey    443:                        if (write(ofd, buf, nr) != nr) {
                    444:                                if (verbose >= 0)
                    445:                                        warn("%s", out);
                    446:                                error++;
                    447:                                break;
                    448:                        }
                    449:        }
                    450:
                    451:        if (ofd < 0 || close(ofd)) {
                    452:                if (!error && verbose >= 0)
                    453:                        warn("%s", out);
                    454:                error++;
                    455:        }
                    456:
1.12      mickey    457:        if (cookie == NULL || (method->close)(cookie) || nr < 0) {
1.1       mickey    458:                if (!error && verbose >= 0)
1.3       mickey    459:                        warn("%s", in);
1.1       mickey    460:                error++;
1.3       mickey    461:                (void) close (ifd);
1.1       mickey    462:        }
                    463:
                    464:        return error;
                    465: }
                    466:
                    467: void
                    468: setfile(name, fs)
                    469:        char *name;
                    470:        register struct stat *fs;
                    471: {
                    472:        static struct timeval tv[2];
                    473:
                    474:        fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
                    475:
                    476:        TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
                    477:        TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
                    478:        if (utimes(name, tv))
                    479:                warn("utimes: %s", name);
                    480:
                    481:        /*
                    482:         * Changing the ownership probably won't succeed, unless we're root
                    483:         * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
                    484:         * the mode; current BSD behavior is to remove all setuid bits on
                    485:         * chown.  If chown fails, lose setuid/setgid bits.
                    486:         */
                    487:        if (chown(name, fs->st_uid, fs->st_gid)) {
                    488:                if (errno != EPERM)
                    489:                        warn("chown: %s", name);
                    490:                fs->st_mode &= ~(S_ISUID|S_ISGID);
                    491:        }
                    492:        if (chmod(name, fs->st_mode))
                    493:                warn("chown: %s", name);
                    494:
1.8       millert   495:        if (fs->st_flags && chflags(name, fs->st_flags))
1.1       mickey    496:                warn("chflags: %s", name);
                    497: }
                    498:
                    499: int
                    500: permission(fname)
                    501:        char *fname;
                    502: {
                    503:        int ch, first;
                    504:
                    505:        if (!isatty(fileno(stderr)))
                    506:                return (0);
                    507:        (void)fprintf(stderr, "overwrite %s? ", fname);
                    508:        first = ch = getchar();
                    509:        while (ch != '\n' && ch != EOF)
                    510:                ch = getchar();
                    511:        return (first == 'y');
                    512: }
                    513:
                    514: void
                    515: usage()
                    516: {
                    517:        fprintf(stderr,
                    518:                "usage: %s [-cdfghlnOtqv] [-b <bits>] [-[0-9]] [file ...]\n",
                    519:                __progname);
                    520:        exit(1);
                    521: }
                    522: