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

Annotation of src/usr.bin/cvs/cvs.c, Revision 1.91

1.91    ! xsa         1: /*     $OpenBSD: cvs.c,v 1.90 2006/01/02 08:11:56 xsa Exp $    */
1.1       jfb         2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.18      tedu        4:  * All rights reserved.
1.1       jfb         5:  *
1.18      tedu        6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
1.1       jfb         9:  *
1.18      tedu       10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
1.1       jfb        12:  * 2. The name of the author may not be used to endorse or promote products
1.18      tedu       13:  *    derived from this software without specific prior written permission.
1.1       jfb        14:  *
                     15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
                     16:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
                     17:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
                     18:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                     19:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                     20:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
                     21:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
                     22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
                     23:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.18      tedu       24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1       jfb        25:  */
                     26:
1.90      xsa        27: #include "includes.h"
1.1       jfb        28:
                     29: #include "cvs.h"
                     30: #include "log.h"
1.8       jfb        31: #include "file.h"
1.1       jfb        32:
                     33:
                     34: extern char *__progname;
                     35:
                     36:
                     37: /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */
                     38: int verbosity = 2;
                     39:
                     40: /* compression level used with zlib, 0 meaning no compression taking place */
                     41: int   cvs_compress = 0;
1.17      jfb        42: int   cvs_readrc = 1;          /* read .cvsrc on startup */
1.1       jfb        43: int   cvs_trace = 0;
                     44: int   cvs_nolog = 0;
                     45: int   cvs_readonly = 0;
1.17      jfb        46: int   cvs_nocase = 0;   /* set to 1 to disable filename case sensitivity */
1.65      xsa        47: int   cvs_noexec = 0;  /* set to 1 to disable disk operations (-n option) */
1.73      joris      48: int   cvs_error = -1;  /* set to the correct error code on failure */
1.17      jfb        49: char *cvs_defargs;             /* default global arguments from .cvsrc */
                     50: char *cvs_command;             /* name of the command we are running */
1.6       jfb        51: int   cvs_cmdop;
1.1       jfb        52: char *cvs_rootstr;
                     53: char *cvs_rsh = CVS_RSH_DEFAULT;
                     54: char *cvs_editor = CVS_EDITOR_DEFAULT;
1.79      xsa        55: char *cvs_homedir = NULL;
                     56: char *cvs_msg = NULL;
1.69      joris      57: char *cvs_repo_base = NULL;
1.80      xsa        58: char *cvs_tmpdir = CVS_TMPDIR_DEFAULT;
1.13      krapht     59:
1.10      jfb        60: /* hierarchy of all the files affected by the command */
                     61: CVSFILE *cvs_files;
                     62:
1.27      jfb        63: static TAILQ_HEAD(, cvs_var) cvs_variables;
                     64:
1.1       jfb        65:
1.75      xsa        66: void           usage(void);
                     67: static void    cvs_read_rcfile(void);
                     68: int            cvs_getopt(int, char **);
1.1       jfb        69:
                     70: /*
                     71:  * usage()
                     72:  *
                     73:  * Display usage information.
                     74:  */
                     75: void
                     76: usage(void)
                     77: {
                     78:        fprintf(stderr,
1.83      xsa        79:            "Usage: %s [-flnQqrtvw] [-d root] [-e editor] [-s var=val] "
1.81      xsa        80:            "[-T tmpdir] [-z level] command [...]\n", __progname);
1.1       jfb        81: }
                     82:
                     83:
                     84: int
                     85: main(int argc, char **argv)
                     86: {
1.17      jfb        87:        char *envstr, *cmd_argv[CVS_CMD_MAXARG], **targv;
                     88:        int i, ret, cmd_argc;
1.1       jfb        89:        struct cvs_cmd *cmdp;
1.79      xsa        90:        struct passwd *pw;
1.80      xsa        91:        struct stat st;
1.87      joris      92:
                     93:        tzset();
1.1       jfb        94:
1.27      jfb        95:        TAILQ_INIT(&cvs_variables);
                     96:
1.91    ! xsa        97:        cvs_log_init(LD_STD, 0);
1.1       jfb        98:
                     99:        /* by default, be very verbose */
                    100:        (void)cvs_log_filter(LP_FILTER_UNSET, LP_INFO);
                    101:
                    102: #ifdef DEBUG
                    103:        (void)cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
                    104: #endif
                    105:
                    106:        /* check environment so command-line options override it */
                    107:        if ((envstr = getenv("CVS_RSH")) != NULL)
                    108:                cvs_rsh = envstr;
                    109:
                    110:        if (((envstr = getenv("CVSEDITOR")) != NULL) ||
                    111:            ((envstr = getenv("VISUAL")) != NULL) ||
                    112:            ((envstr = getenv("EDITOR")) != NULL))
                    113:                cvs_editor = envstr;
1.76      xsa       114:
                    115:        if ((envstr = getenv("CVSREAD")) != NULL)
                    116:                cvs_readonly = 1;
1.1       jfb       117:
1.79      xsa       118:        if ((cvs_homedir = getenv("HOME")) == NULL) {
1.89      xsa       119:                if ((pw = getpwuid(getuid())) == NULL)
                    120:                        fatal("getpwuid failed");
1.79      xsa       121:                cvs_homedir = pw->pw_dir;
1.85      reyk      122:        }
1.79      xsa       123:
1.80      xsa       124:        if ((envstr = getenv("TMPDIR")) != NULL)
                    125:                cvs_tmpdir = envstr;
                    126:
1.17      jfb       127:        ret = cvs_getopt(argc, argv);
                    128:
                    129:        argc -= ret;
                    130:        argv += ret;
                    131:        if (argc == 0) {
                    132:                usage();
1.82      xsa       133:                exit(CVS_EX_USAGE);
1.17      jfb       134:        }
1.80      xsa       135:
1.17      jfb       136:        cvs_command = argv[0];
                    137:
1.80      xsa       138:        /*
                    139:         * check the tmp dir, either specified through
                    140:         * the environment variable TMPDIR, or via
                    141:         * the global option -T <dir>
                    142:         */
                    143:        if (stat(cvs_tmpdir, &st) == -1) {
                    144:                cvs_log(LP_ERR, "failed to stat `%s'", cvs_tmpdir);
1.82      xsa       145:                exit(CVS_EX_FILE);
1.80      xsa       146:        } else if (!S_ISDIR(st.st_mode)) {
                    147:                cvs_log(LP_ERR, "`%s' is not valid temporary directory",
                    148:                    cvs_tmpdir);
1.82      xsa       149:                exit(CVS_EX_FILE);
1.80      xsa       150:        }
                    151:
1.74      xsa       152:        if (cvs_readrc == 1) {
1.17      jfb       153:                cvs_read_rcfile();
                    154:
                    155:                if (cvs_defargs != NULL) {
                    156:                        targv = cvs_makeargv(cvs_defargs, &i);
                    157:                        if (targv == NULL) {
                    158:                                cvs_log(LP_ERR,
                    159:                                    "failed to load default arguments to %s",
                    160:                                    __progname);
1.82      xsa       161:                                exit(CVS_EX_DATA);
1.17      jfb       162:                        }
                    163:
                    164:                        cvs_getopt(i, targv);
                    165:                        cvs_freeargv(targv, i);
1.88      joris     166:                        xfree(targv);
1.17      jfb       167:                }
                    168:        }
                    169:
                    170:        /* setup signal handlers */
                    171:        signal(SIGPIPE, SIG_IGN);
                    172:
1.40      jfb       173:        if (cvs_file_init() < 0) {
                    174:                cvs_log(LP_ERR, "failed to initialize file support");
1.82      xsa       175:                exit(CVS_EX_FILE);
1.40      jfb       176:        }
1.17      jfb       177:
                    178:        ret = -1;
                    179:
                    180:        cmdp = cvs_findcmd(cvs_command);
                    181:        if (cmdp == NULL) {
                    182:                fprintf(stderr, "Unknown command: `%s'\n\n", cvs_command);
                    183:                fprintf(stderr, "CVS commands are:\n");
1.67      jfb       184:                for (i = 0; cvs_cdt[i] != NULL; i++)
1.17      jfb       185:                        fprintf(stderr, "\t%-16s%s\n",
1.67      jfb       186:                            cvs_cdt[i]->cmd_name, cvs_cdt[i]->cmd_descr);
                    187:                exit(CVS_EX_USAGE);
1.17      jfb       188:        }
                    189:
                    190:        cvs_cmdop = cmdp->cmd_op;
                    191:
                    192:        cmd_argc = 0;
                    193:        memset(cmd_argv, 0, sizeof(cmd_argv));
                    194:
                    195:        cmd_argv[cmd_argc++] = argv[0];
                    196:        if (cmdp->cmd_defargs != NULL) {
                    197:                /* transform into a new argument vector */
                    198:                ret = cvs_getargv(cmdp->cmd_defargs, cmd_argv + 1,
                    199:                    CVS_CMD_MAXARG - 1);
                    200:                if (ret < 0) {
                    201:                        cvs_log(LP_ERRNO, "failed to generate argument vector "
                    202:                            "from default arguments");
1.82      xsa       203:                        exit(CVS_EX_DATA);
1.17      jfb       204:                }
                    205:                cmd_argc += ret;
                    206:        }
                    207:        for (ret = 1; ret < argc; ret++)
                    208:                cmd_argv[cmd_argc++] = argv[ret];
                    209:
1.45      joris     210:        ret = cvs_startcmd(cmdp, cmd_argc, cmd_argv);
1.51      joris     211:        switch (ret) {
                    212:        case CVS_EX_USAGE:
1.56      jfb       213:                fprintf(stderr, "Usage: %s %s %s\n", __progname, cvs_command,
                    214:                    cmdp->cmd_synopsis);
1.51      joris     215:                break;
                    216:        case CVS_EX_DATA:
1.55      jfb       217:                cvs_log(LP_ABORT, "internal data error");
1.51      joris     218:                break;
                    219:        case CVS_EX_PROTO:
1.55      jfb       220:                cvs_log(LP_ABORT, "protocol error");
1.51      joris     221:                break;
                    222:        case CVS_EX_FILE:
1.55      jfb       223:                cvs_log(LP_ABORT, "an operation on a file or directory failed");
1.60      jfb       224:                break;
                    225:        case CVS_EX_BADROOT:
1.71      xsa       226:                /* match GNU CVS output, thus the LP_ERR and LP_ABORT codes. */
                    227:                cvs_log(LP_ERR,
1.60      jfb       228:                    "No CVSROOT specified! Please use the `-d' option");
                    229:                cvs_log(LP_ABORT,
                    230:                    "or set the CVSROOT enviroment variable.");
1.73      joris     231:                break;
                    232:        case CVS_EX_ERR:
                    233:                cvs_log(LP_ABORT, "yeah, we failed, and we don't know why");
1.51      joris     234:                break;
                    235:        default:
                    236:                break;
1.17      jfb       237:        }
                    238:
                    239:        if (cvs_files != NULL)
                    240:                cvs_file_free(cvs_files);
1.33      jfb       241:        if (cvs_msg != NULL)
1.88      joris     242:                xfree(cvs_msg);
1.17      jfb       243:
                    244:        return (ret);
                    245: }
                    246:
                    247:
                    248: int
                    249: cvs_getopt(int argc, char **argv)
                    250: {
                    251:        int ret;
                    252:        char *ep;
                    253:
1.83      xsa       254:        while ((ret = getopt(argc, argv, "b:d:e:fHlnQqrs:T:tvwz:")) != -1) {
1.1       jfb       255:                switch (ret) {
1.11      jfb       256:                case 'b':
                    257:                        /*
                    258:                         * We do not care about the bin directory for RCS files
                    259:                         * as this program has no dependencies on RCS programs,
                    260:                         * so it is only here for backwards compatibility.
                    261:                         */
                    262:                        cvs_log(LP_NOTICE, "the -b argument is obsolete");
                    263:                        break;
1.1       jfb       264:                case 'd':
                    265:                        cvs_rootstr = optarg;
                    266:                        break;
                    267:                case 'e':
                    268:                        cvs_editor = optarg;
                    269:                        break;
                    270:                case 'f':
1.17      jfb       271:                        cvs_readrc = 0;
1.1       jfb       272:                        break;
                    273:                case 'l':
                    274:                        cvs_nolog = 1;
                    275:                        break;
                    276:                case 'n':
1.65      xsa       277:                        cvs_noexec = 1;
1.1       jfb       278:                        break;
                    279:                case 'Q':
                    280:                        verbosity = 0;
                    281:                        break;
                    282:                case 'q':
                    283:                        /* don't override -Q */
                    284:                        if (verbosity > 1)
                    285:                                verbosity = 1;
                    286:                        break;
                    287:                case 'r':
                    288:                        cvs_readonly = 1;
                    289:                        break;
1.27      jfb       290:                case 's':
                    291:                        ep = strchr(optarg, '=');
                    292:                        if (ep == NULL) {
                    293:                                cvs_log(LP_ERR, "no = in variable assignment");
1.82      xsa       294:                                exit(CVS_EX_USAGE);
1.27      jfb       295:                        }
                    296:                        *(ep++) = '\0';
                    297:                        if (cvs_var_set(optarg, ep) < 0)
1.82      xsa       298:                                exit(CVS_EX_USAGE);
1.80      xsa       299:                        break;
                    300:                case 'T':
                    301:                        cvs_tmpdir = optarg;
1.27      jfb       302:                        break;
1.1       jfb       303:                case 't':
1.24      jfb       304:                        (void)cvs_log_filter(LP_FILTER_UNSET, LP_TRACE);
1.1       jfb       305:                        cvs_trace = 1;
                    306:                        break;
                    307:                case 'v':
                    308:                        printf("%s\n", CVS_VERSION);
                    309:                        exit(0);
                    310:                        /* NOTREACHED */
1.83      xsa       311:                        break;
                    312:                case 'w':
                    313:                        cvs_readonly = 0;
1.11      jfb       314:                        break;
                    315:                case 'x':
                    316:                        /*
                    317:                         * Kerberos encryption support, kept for compatibility
                    318:                         */
1.1       jfb       319:                        break;
                    320:                case 'z':
1.18      tedu      321:                        cvs_compress = (int)strtol(optarg, &ep, 10);
1.1       jfb       322:                        if (*ep != '\0')
1.91    ! xsa       323:                                fatal("error parsing compression level");
1.1       jfb       324:                        if (cvs_compress < 0 || cvs_compress > 9)
1.91    ! xsa       325:                                fatal("gzip compression level must be "
1.1       jfb       326:                                    "between 0 and 9");
                    327:                        break;
                    328:                default:
                    329:                        usage();
1.82      xsa       330:                        exit(CVS_EX_USAGE);
1.1       jfb       331:                }
                    332:        }
                    333:
1.17      jfb       334:        ret = optind;
1.1       jfb       335:        optind = 1;
1.17      jfb       336:        optreset = 1;   /* for next call */
1.12      jfb       337:
1.1       jfb       338:        return (ret);
                    339: }
                    340:
                    341:
                    342: /*
1.17      jfb       343:  * cvs_read_rcfile()
1.1       jfb       344:  *
                    345:  * Read the CVS `.cvsrc' file in the user's home directory.  If the file
                    346:  * exists, it should contain a list of arguments that should always be given
                    347:  * implicitly to the specified commands.
                    348:  */
1.42      joris     349: static void
1.17      jfb       350: cvs_read_rcfile(void)
1.1       jfb       351: {
1.79      xsa       352:        char rcpath[MAXPATHLEN], linebuf[128], *lp, *p;
1.54      xsa       353:        int l, linenum = 0;
1.17      jfb       354:        size_t len;
1.1       jfb       355:        struct cvs_cmd *cmdp;
                    356:        FILE *fp;
                    357:
1.79      xsa       358:        l = snprintf(rcpath, sizeof(rcpath), "%s/%s", cvs_homedir, CVS_PATH_RC);
1.54      xsa       359:        if (l == -1 || l >= (int)sizeof(rcpath)) {
                    360:                errno = ENAMETOOLONG;
                    361:                cvs_log(LP_ERRNO, "%s", rcpath);
                    362:                return;
                    363:        }
1.1       jfb       364:
                    365:        fp = fopen(rcpath, "r");
                    366:        if (fp == NULL) {
                    367:                if (errno != ENOENT)
                    368:                        cvs_log(LP_NOTICE, "failed to open `%s': %s", rcpath,
                    369:                            strerror(errno));
                    370:                return;
                    371:        }
                    372:
1.84      xsa       373:        while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) {
1.23      xsa       374:                linenum++;
1.17      jfb       375:                if ((len = strlen(linebuf)) == 0)
                    376:                        continue;
                    377:                if (linebuf[len - 1] != '\n') {
1.23      xsa       378:                        cvs_log(LP_WARN, "line too long in `%s:%d'", rcpath,
                    379:                                linenum);
1.17      jfb       380:                        break;
                    381:                }
                    382:                linebuf[--len] = '\0';
                    383:
1.70      joris     384:                /* skip any whitespaces */
                    385:                p = linebuf;
                    386:                while (*p == ' ')
                    387:                        *p++;
                    388:
                    389:                /* allow comments */
                    390:                if (*p == '#')
                    391:                        continue;
                    392:
                    393:                lp = strchr(p, ' ');
1.1       jfb       394:                if (lp == NULL)
1.17      jfb       395:                        continue;       /* ignore lines with no arguments */
                    396:                *lp = '\0';
1.70      joris     397:                if (strcmp(p, "cvs") == 0) {
1.17      jfb       398:                        /*
                    399:                         * Global default options.  In the case of cvs only,
                    400:                         * we keep the 'cvs' string as first argument because
                    401:                         * getopt() does not like starting at index 0 for
                    402:                         * argument processing.
                    403:                         */
                    404:                        *lp = ' ';
1.88      joris     405:                        cvs_defargs = xstrdup(p);
1.15      deraadt   406:                } else {
1.17      jfb       407:                        lp++;
1.70      joris     408:                        cmdp = cvs_findcmd(p);
1.1       jfb       409:                        if (cmdp == NULL) {
                    410:                                cvs_log(LP_NOTICE,
1.25      xsa       411:                                    "unknown command `%s' in `%s:%d'",
1.70      joris     412:                                    p, rcpath, linenum);
1.1       jfb       413:                                continue;
                    414:                        }
1.17      jfb       415:
1.88      joris     416:                        cmdp->cmd_defargs = xstrdup(lp);
1.1       jfb       417:                }
                    418:        }
                    419:        if (ferror(fp)) {
1.23      xsa       420:                cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath);
1.1       jfb       421:        }
                    422:
                    423:        (void)fclose(fp);
1.27      jfb       424: }
                    425:
                    426:
                    427: /*
                    428:  * cvs_var_set()
                    429:  *
                    430:  * Set the value of the variable <var> to <val>.  If there is no such variable,
                    431:  * a new entry is created, otherwise the old value is overwritten.
                    432:  * Returns 0 on success, or -1 on failure.
                    433:  */
                    434: int
                    435: cvs_var_set(const char *var, const char *val)
                    436: {
                    437:        char *valcp;
                    438:        const char *cp;
                    439:        struct cvs_var *vp;
                    440:
                    441:        if ((var == NULL) || (*var == '\0')) {
                    442:                cvs_log(LP_ERR, "no variable name");
                    443:                return (-1);
                    444:        }
                    445:
                    446:        /* sanity check on the name */
                    447:        for (cp = var; *cp != '\0'; cp++)
                    448:                if (!isalnum(*cp) && (*cp != '_')) {
                    449:                        cvs_log(LP_ERR,
                    450:                            "variable name `%s' contains invalid characters",
                    451:                            var);
                    452:                        return (-1);
                    453:                }
                    454:
                    455:        TAILQ_FOREACH(vp, &cvs_variables, cv_link)
                    456:                if (strcmp(vp->cv_name, var) == 0)
                    457:                        break;
                    458:
1.88      joris     459:        valcp = xstrdup(val);
1.27      jfb       460:        if (vp == NULL) {
1.88      joris     461:                vp = (struct cvs_var *)xmalloc(sizeof(*vp));
1.27      jfb       462:                memset(vp, 0, sizeof(*vp));
                    463:
1.88      joris     464:                vp->cv_name = xstrdup(var);
1.27      jfb       465:                TAILQ_INSERT_TAIL(&cvs_variables, vp, cv_link);
                    466:
                    467:        } else  /* free the previous value */
1.88      joris     468:                xfree(vp->cv_val);
1.27      jfb       469:
                    470:        vp->cv_val = valcp;
                    471:
                    472:        return (0);
                    473: }
                    474:
                    475:
                    476: /*
                    477:  * cvs_var_set()
                    478:  *
                    479:  * Remove any entry for the variable <var>.
                    480:  * Returns 0 on success, or -1 on failure.
                    481:  */
                    482: int
                    483: cvs_var_unset(const char *var)
                    484: {
                    485:        struct cvs_var *vp;
                    486:
                    487:        TAILQ_FOREACH(vp, &cvs_variables, cv_link)
                    488:                if (strcmp(vp->cv_name, var) == 0) {
                    489:                        TAILQ_REMOVE(&cvs_variables, vp, cv_link);
1.88      joris     490:                        xfree(vp->cv_name);
                    491:                        xfree(vp->cv_val);
                    492:                        xfree(vp);
1.27      jfb       493:                        return (0);
                    494:                }
                    495:
                    496:        return (-1);
                    497:
                    498: }
                    499:
                    500:
                    501: /*
                    502:  * cvs_var_get()
                    503:  *
                    504:  * Get the value associated with the variable <var>.  Returns a pointer to the
                    505:  * value string on success, or NULL if the variable does not exist.
                    506:  */
                    507:
1.75      xsa       508: const char *
1.27      jfb       509: cvs_var_get(const char *var)
                    510: {
                    511:        struct cvs_var *vp;
                    512:
                    513:        TAILQ_FOREACH(vp, &cvs_variables, cv_link)
                    514:                if (strcmp(vp->cv_name, var) == 0)
                    515:                        return (vp->cv_val);
                    516:
                    517:        return (NULL);
1.1       jfb       518: }