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

Annotation of src/usr.bin/rcs/ci.c, Revision 1.63

1.63    ! niallo      1: /*     $OpenBSD: ci.c,v 1.62 2005/11/17 00:22:30 niallo Exp $  */
1.1       niallo      2: /*
                      3:  * Copyright (c) 2005 Niall O'Higgins <niallo@openbsd.org>
                      4:  * All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following cinditions
                      8:  * are met:
                      9:  *
                     10:  * 1. Redistributions of source cide must retain the above cipyright
                     11:  *    notice, this list of cinditions and the following disclaimer.
                     12:  * 2. The name of the author may not be used to endorse or promote products
                     13:  *    derived from this software without specific prior written permission.
                     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
                     24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     25:  */
                     26:
                     27: #include <sys/param.h>
                     28: #include <sys/types.h>
                     29: #include <sys/stat.h>
                     30: #include <sys/wait.h>
                     31:
                     32: #include <err.h>
                     33: #include <pwd.h>
                     34: #include <errno.h>
                     35: #include <stdio.h>
                     36: #include <ctype.h>
                     37: #include <stdlib.h>
                     38: #include <unistd.h>
                     39: #include <string.h>
1.20      niallo     40: #include <time.h>
1.1       niallo     41:
                     42: #include "log.h"
                     43: #include "rcs.h"
1.4       niallo     44: #include "diff.h"
1.1       niallo     45: #include "rcsprog.h"
1.9       niallo     46:
1.58      niallo     47: #define CI_OPTSTRING    "d::f::i::j::k:l::m:M::N:n:qr::s:u::Vw:"
1.25      niallo     48: #define DATE_NOW        -1
                     49: #define DATE_MTIME      -2
                     50:
1.58      niallo     51: #define LOG_INIT        "Initial revision"
1.63    ! niallo     52: #define LOG_PROMPT      "enter log message, terminated with a single '.' "    \
1.58      niallo     53:                         "or end of file:\n>> "
1.63    ! niallo     54: #define DESC_PROMPT     "enter description, terminated with single '.' "      \
        !            55:                        "or end of file:\nNOTE: This is NOT the log message!" \
        !            56:                         "\n>> "
1.58      niallo     57:
                     58: struct checkin_params {
                     59:        int flags, openflags;
                     60:        mode_t fmode;
                     61:        time_t date;
                     62:        RCSFILE *file;
                     63:        RCSNUM *frev, *newrev;
                     64:        char fpath[MAXPATHLEN], *rcs_msg, *username, *deltatext, *filename;
                     65:        const char *symbol, *state;
                     66: };
                     67:
                     68: static int    checkin_attach_symbol(struct checkin_params *pb);
                     69: static int    checkin_checklock(struct checkin_params *pb);
1.63    ! niallo     70: static char * checkin_choose_rcsfile(const char *);
1.58      niallo     71: static char * checkin_diff_file(struct checkin_params *);
                     72: static char * checkin_getdesc(void);
                     73: static char * checkin_getinput(const char *);
1.34      niallo     74: static char * checkin_getlogmsg(RCSNUM *, RCSNUM *);
1.63    ! niallo     75: static int    checkin_init(struct checkin_params *);
        !            76: static int    checkin_mtimedate(struct checkin_params *pb);
        !            77: static int    checkin_update(struct checkin_params *pb);
1.58      niallo     78: static void   checkin_revert(struct checkin_params *pb);
1.4       niallo     79:
1.1       niallo     80: void
                     81: checkin_usage(void)
                     82: {
                     83:        fprintf(stderr,
1.58      niallo     84:            "usage: ci [-MNqV] [-d[date]] [-f[rev]] [-i[rev]] [-j[rev]]\n"
                     85:             "          [-kmode] [-l[rev]] [-M[rev]] [-mmsg] [-Nsymbol]\n"
                     86:             "          [-nsymbol] [-r[rev]] [-sstate] [-u[rev]] [-wusername]\n"
                     87:             "          file ...\n");
                     88: }
                     89:
1.55      niallo     90:
1.1       niallo     91:
                     92: /*
                     93:  * checkin_main()
                     94:  *
                     95:  * Handler for the `ci' program.
                     96:  * Returns 0 on success, or >0 on error.
                     97:  */
                     98: int
                     99: checkin_main(int argc, char **argv)
                    100: {
1.58      niallo    101:        int i, ch, status;
                    102:        struct checkin_params pb;
1.1       niallo    103:
1.58      niallo    104:        pb.date = DATE_NOW;
                    105:        pb.file = NULL;
                    106:        pb.rcs_msg = pb.username = NULL;
                    107:        pb.state = pb.symbol = NULL;
                    108:        pb.newrev =  NULL;
                    109:        pb.fmode = pb.flags = status = 0;
1.1       niallo    110:
1.58      niallo    111:        pb.flags = INTERACTIVE;
                    112:        pb.openflags = RCS_RDWR|RCS_CREATE;
1.9       niallo    113:
1.58      niallo    114:        while ((ch = rcs_getopt(argc, argv, CI_OPTSTRING)) != -1) {
1.1       niallo    115:                switch (ch) {
1.20      niallo    116:                case 'd':
1.25      niallo    117:                        if (rcs_optarg == NULL)
1.58      niallo    118:                                pb.date = DATE_MTIME;
                    119:                        else if ((pb.date = cvs_date_parse(rcs_optarg)) <= 0) {
1.20      niallo    120:                                cvs_log(LP_ERR, "invalide date");
                    121:                                exit(1);
                    122:                        }
                    123:                        break;
1.29      niallo    124:                case 'f':
1.58      niallo    125:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    126:                        pb.flags |= FORCE;
1.29      niallo    127:                        break;
1.1       niallo    128:                case 'h':
                    129:                        (usage)();
                    130:                        exit(0);
1.58      niallo    131:                case 'i':
                    132:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    133:                        pb.openflags |= RCS_CREATE;
                    134:                        break;
                    135:                case 'j':
                    136:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    137:                        pb.openflags &= ~RCS_CREATE;
                    138:                        break;
1.30      niallo    139:                case 'l':
1.58      niallo    140:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    141:                        pb.flags |= CO_LOCK;
1.54      niallo    142:                        break;
                    143:                case 'M':
1.58      niallo    144:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    145:                        pb.flags |= CO_REVDATE;
1.30      niallo    146:                        break;
1.1       niallo    147:                case 'm':
1.58      niallo    148:                        pb.rcs_msg = rcs_optarg;
                    149:                        if (pb.rcs_msg == NULL) {
                    150:                                cvs_log(LP_ERR,
                    151:                                    "missing message for -m option");
                    152:                                exit(1);
                    153:                        }
                    154:                        pb.flags &= ~INTERACTIVE;
1.3       joris     155:                        break;
1.42      niallo    156:                case 'N':
1.58      niallo    157:                        if ((pb.symbol = strdup(rcs_optarg)) == NULL) {
1.42      niallo    158:                                cvs_log(LP_ERRNO, "out of memory");
                    159:                                exit(1);
                    160:                        }
1.58      niallo    161:                        if (rcs_sym_check(pb.symbol) != 1) {
                    162:                                cvs_log(LP_ERR, "invalid symbol `%s'",
                    163:                                    pb.symbol);
1.42      niallo    164:                                exit(1);
                    165:                        }
1.58      niallo    166:                        pb.flags |= CI_SYMFORCE;
1.42      niallo    167:                        break;
1.38      niallo    168:                case 'n':
1.58      niallo    169:                        if ((pb.symbol = strdup(rcs_optarg)) == NULL) {
1.38      niallo    170:                                cvs_log(LP_ERRNO, "out of memory");
                    171:                                exit(1);
                    172:                        }
1.58      niallo    173:                        if (rcs_sym_check(pb.symbol) != 1) {
                    174:                                cvs_log(LP_ERR, "invalid symbol `%s'",
                    175:                                    pb.symbol);
1.38      niallo    176:                                exit(1);
                    177:                        }
                    178:                        break;
1.3       joris     179:                case 'q':
                    180:                        verbose = 0;
1.1       niallo    181:                        break;
1.30      niallo    182:                case 'r':
1.58      niallo    183:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    184:                        pb.flags |= CI_DEFAULT;
1.9       niallo    185:                        break;
1.51      niallo    186:                case 's':
1.58      niallo    187:                        pb.state = rcs_optarg;
                    188:                        if (rcs_state_check(pb.state) < 0) {
                    189:                                cvs_log(LP_ERR, "invalid state `%s'",
                    190:                                    pb.state);
1.51      niallo    191:                                exit(1);
                    192:                        }
                    193:                        break;
1.9       niallo    194:                case 'u':
1.58      niallo    195:                        rcs_set_rev(rcs_optarg, &pb.newrev);
                    196:                        pb.flags |= CO_UNLOCK;
1.9       niallo    197:                        break;
1.30      niallo    198:                case 'V':
                    199:                        printf("%s\n", rcs_version);
                    200:                        exit(0);
1.31      niallo    201:                case 'w':
1.58      niallo    202:                        pb.username = rcs_optarg;
1.31      niallo    203:                        break;
1.1       niallo    204:                default:
                    205:                        (usage)();
                    206:                        exit(1);
                    207:                }
                    208:        }
                    209:
1.24      joris     210:        argc -= rcs_optind;
                    211:        argv += rcs_optind;
                    212:
1.1       niallo    213:        if (argc == 0) {
                    214:                cvs_log(LP_ERR, "no input file");
                    215:                (usage)();
                    216:                exit(1);
                    217:        }
                    218:
1.58      niallo    219:        if ((pb.username == NULL) && (pb.username = getlogin()) == NULL) {
1.31      niallo    220:                cvs_log(LP_ERRNO, "failed to get username");
                    221:                exit(1);
                    222:        }
                    223:
                    224:
1.1       niallo    225:        for (i = 0; i < argc; i++) {
1.58      niallo    226:                pb.filename = argv[i];
1.1       niallo    227:
1.58      niallo    228:                /*
                    229:                 * Test for existence of ,v file. If we are expected to
                    230:                 * create one, set NEWFILE flag.
                    231:                 */
1.63    ! niallo    232:                if ((pb.openflags & RCS_CREATE)
        !           233:                    && (rcs_statfile(pb.filename, pb.fpath,
        !           234:                            sizeof(pb.fpath)) < 0))
1.58      niallo    235:                        pb.flags |= NEWFILE;
                    236:                else
                    237:                        pb.openflags &= ~RCS_CREATE;
                    238:
1.63    ! niallo    239:                /*
        !           240:                 * If we are to create a new ,v file, we must decide where it
        !           241:                 * should go.
        !           242:                 */
        !           243:                if (pb.flags & NEWFILE) {
        !           244:                        char *fpath = checkin_choose_rcsfile(pb.filename);
        !           245:                        if (fpath == NULL) {
        !           246:                                status = 1;
        !           247:                                continue;
        !           248:                        }
        !           249:                        strlcpy(pb.fpath, fpath, sizeof(pb.fpath));
        !           250:                        free(fpath);
        !           251:                }
        !           252:
1.58      niallo    253:                pb.file = rcs_open(pb.fpath, pb.openflags, pb.fmode);
                    254:
                    255:                if (pb.file == NULL) {
1.60      niallo    256:                        cvs_log(LP_ERR, "failed to open rcsfile '%s'",
                    257:                            pb.fpath);
1.1       niallo    258:                        exit(1);
                    259:                }
1.29      niallo    260:
1.58      niallo    261:                if (verbose == 1)
                    262:                        printf("%s  <--  %s\n", pb.fpath, pb.filename);
1.61      niallo    263:
1.63    ! niallo    264:                if (pb.flags & NEWFILE)
        !           265:                        status = checkin_init(&pb);
1.15      niallo    266:                else
1.63    ! niallo    267:                        status = checkin_update(&pb);
1.4       niallo    268:        }
                    269:
1.16      niallo    270:        return (status);
1.4       niallo    271: }
                    272:
1.59      niallo    273: /*
                    274:  * checkin_diff_file()
                    275:  *
                    276:  * Generate the diff between the working file and a revision.
                    277:  * Returns pointer to a char array on success, NULL on failure.
                    278:  */
1.4       niallo    279: static char *
1.58      niallo    280: checkin_diff_file(struct checkin_params *pb)
1.4       niallo    281: {
                    282:        char path1[MAXPATHLEN], path2[MAXPATHLEN];
                    283:        BUF *b1, *b2, *b3;
                    284:        char rbuf[64], *deltatext;
                    285:
1.58      niallo    286:        rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
1.4       niallo    287:
1.58      niallo    288:        if ((b1 = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
                    289:                cvs_log(LP_ERR, "failed to load file: '%s'", pb->filename);
1.4       niallo    290:                return (NULL);
1.1       niallo    291:        }
                    292:
1.58      niallo    293:        if ((b2 = rcs_getrev(pb->file, pb->frev)) == NULL) {
1.4       niallo    294:                cvs_log(LP_ERR, "failed to load revision");
                    295:                cvs_buf_free(b1);
                    296:                return (NULL);
                    297:        }
                    298:
1.57      xsa       299:        if ((b3 = cvs_buf_alloc((size_t)128, BUF_AUTOEXT)) == NULL) {
1.4       niallo    300:                cvs_log(LP_ERR, "failed to allocated buffer for diff");
                    301:                cvs_buf_free(b1);
                    302:                cvs_buf_free(b2);
                    303:                return (NULL);
                    304:        }
                    305:
1.50      xsa       306:        strlcpy(path1, rcs_tmpdir, sizeof(path1));
                    307:        strlcat(path1, "/diff1.XXXXXXXXXX", sizeof(path1));
1.4       niallo    308:        if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
                    309:                cvs_log(LP_ERRNO, "could not write temporary file");
                    310:                cvs_buf_free(b1);
                    311:                cvs_buf_free(b2);
                    312:                return (NULL);
                    313:        }
                    314:        cvs_buf_free(b1);
                    315:
1.50      xsa       316:        strlcpy(path2, rcs_tmpdir, sizeof(path2));
                    317:        strlcat(path2, "/diff2.XXXXXXXXXX", sizeof(path2));
1.4       niallo    318:        if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
                    319:                cvs_buf_free(b2);
                    320:                (void)unlink(path1);
                    321:                return (NULL);
                    322:        }
                    323:        cvs_buf_free(b2);
                    324:
1.5       niallo    325:        diff_format = D_RCSDIFF;
1.4       niallo    326:        cvs_diffreg(path1, path2, b3);
                    327:        (void)unlink(path1);
                    328:        (void)unlink(path2);
                    329:
                    330:        cvs_buf_putc(b3, '\0');
                    331:        deltatext = (char *)cvs_buf_release(b3);
                    332:
                    333:        return (deltatext);
1.6       niallo    334: }
                    335:
                    336: /*
1.59      niallo    337:  * checkin_getlogmsg()
                    338:  *
1.6       niallo    339:  * Get log message from user interactively.
1.59      niallo    340:  * Returns pointer to a char array on success, NULL on failure.
1.6       niallo    341:  */
                    342: static char *
1.34      niallo    343: checkin_getlogmsg(RCSNUM *rev, RCSNUM *rev2)
1.6       niallo    344: {
1.58      niallo    345:        char   *rcs_msg, nrev[16], prev[16];
1.6       niallo    346:        RCSNUM *tmprev;
                    347:
1.7       niallo    348:        rcs_msg = NULL;
1.6       niallo    349:        tmprev = rcsnum_alloc();
                    350:        rcsnum_cpy(rev, tmprev, 16);
1.9       niallo    351:        rcsnum_tostr(tmprev, prev, sizeof(prev));
1.12      niallo    352:        if (rev2 == NULL)
                    353:                rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
                    354:        else
                    355:                rcsnum_tostr(rev2, nrev, sizeof(nrev));
1.6       niallo    356:        rcsnum_free(tmprev);
                    357:
1.47      niallo    358:        if (verbose == 1)
                    359:                printf("new revision: %s; previous revision: %s\n", nrev,
                    360:                    prev);
1.32      joris     361:
1.58      niallo    362:        rcs_msg = checkin_getinput(LOG_PROMPT);
                    363:        return (rcs_msg);
                    364: }
                    365:
                    366:
                    367: /*
                    368:  * checkin_getdesc()
                    369:  *
                    370:  * Get file description interactively.
1.59      niallo    371:  * Returns pointer to a char array on success, NULL on failure.
1.58      niallo    372:  */
                    373: static char *
                    374: checkin_getdesc()
                    375: {
                    376:        char *description;
                    377:
                    378:        description = checkin_getinput(DESC_PROMPT);
                    379:        return (description);
                    380: }
                    381:
                    382: /*
                    383:  * checkin_getinput()
                    384:  *
1.59      niallo    385:  * Get some input from the user, in RCS style, prompting with message <prompt>.
                    386:  * Returns pointer to a char array on success, NULL on failure.
1.58      niallo    387:  */
                    388: static char *
                    389: checkin_getinput(const char *prompt)
                    390: {
                    391:        BUF *inputbuf;
                    392:        char *input, buf[128];
                    393:
                    394:        if ((inputbuf = cvs_buf_alloc((size_t)64, BUF_AUTOEXT)) == NULL) {
                    395:                cvs_log(LP_ERR, "failed to allocate input buffer");
                    396:                return (NULL);
                    397:        }
                    398:
                    399:        printf(prompt);
1.6       niallo    400:        for (;;) {
                    401:                fgets(buf, (int)sizeof(buf), stdin);
1.9       niallo    402:                if (feof(stdin) || ferror(stdin) || buf[0] == '.')
1.6       niallo    403:                        break;
1.58      niallo    404:                cvs_buf_append(inputbuf, buf, strlen(buf));
1.46      joris     405:                printf(">> ");
1.6       niallo    406:        }
1.32      joris     407:
1.58      niallo    408:        cvs_buf_putc(inputbuf, '\0');
                    409:        input = (char *)cvs_buf_release(inputbuf);
                    410:
                    411:        return (input);
                    412: }
                    413:
                    414: /*
1.63    ! niallo    415:  * checkin_update()
        !           416:  *
        !           417:  * Do a checkin to an existing RCS file.
        !           418:  *
        !           419:  * On success, return 0. On error return -1.
        !           420:  */
        !           421: static int
        !           422: checkin_update(struct checkin_params *pb)
        !           423: {
        !           424:        char  *filec;
        !           425:        BUF *bp;
        !           426:
        !           427:        pb->frev = pb->file->rf_head;
        !           428:
        !           429:        /*
        !           430:         * If revision passed on command line is less than HEAD, bail.
        !           431:         */
        !           432:        if ((pb->newrev != NULL)
        !           433:            && (rcsnum_cmp(pb->newrev, pb->frev, 0) > 0)) {
        !           434:                cvs_log(LP_ERR, "revision is too low!");
        !           435:                rcs_close(pb->file);
        !           436:                return (-1);
        !           437:        }
        !           438:
        !           439:        /*
        !           440:         * Load file contents
        !           441:         */
        !           442:        if ((bp = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
        !           443:                cvs_log(LP_ERR, "failed to load '%s'", pb->filename);
        !           444:                return (-1);
        !           445:        }
        !           446:
        !           447:        if (cvs_buf_putc(bp, '\0') < 0)
        !           448:                return (-1);
        !           449:
        !           450:        filec = (char *)cvs_buf_release(bp);
        !           451:
        !           452:        /*
        !           453:         * Get RCS patch
        !           454:         */
        !           455:        if ((pb->deltatext = checkin_diff_file(pb)) == NULL) {
        !           456:                cvs_log(LP_ERR, "failed to get diff");
        !           457:                return (-1);
        !           458:        }
        !           459:
        !           460:        /*
        !           461:         * If -f is not specified and there are no differences, tell
        !           462:         * the user and revert to latest version.
        !           463:         */
        !           464:        if (!(pb->flags & FORCE) && (strlen(pb->deltatext) < 1)) {
        !           465:                checkin_revert(pb);
        !           466:                return (0);
        !           467:        }
        !           468:
        !           469:        /*
        !           470:         * Check for a lock belonging to this user. If none,
        !           471:         * abort check-in.
        !           472:         */
        !           473:        if (checkin_checklock(pb) < 0)
        !           474:                return (-1);
        !           475:
        !           476:        /*
        !           477:         * If no log message specified, get it interactively.
        !           478:         */
        !           479:        if (pb->flags & INTERACTIVE)
        !           480:                pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev);
        !           481:
        !           482:        /*
        !           483:         * Remove the lock
        !           484:         */
        !           485:        if (rcs_lock_remove(pb->file, pb->frev) < 0) {
        !           486:                if (rcs_errno != RCS_ERR_NOENT)
        !           487:                    cvs_log(LP_WARN, "failed to remove lock");
        !           488:        }
        !           489:
        !           490:        /*
        !           491:         * Current head revision gets the RCS patch as rd_text
        !           492:         */
        !           493:        if (rcs_deltatext_set(pb->file, pb->frev, pb->deltatext) == -1) {
        !           494:                cvs_log(LP_ERR,
        !           495:                    "failed to set new rd_text for head rev");
        !           496:                exit (1);
        !           497:        }
        !           498:
        !           499:        /*
        !           500:         * Set the date of the revision to be the last modification
        !           501:         * time of the working file if -d has no argument.
        !           502:         */
        !           503:        if (pb->date == DATE_MTIME
        !           504:            && (checkin_mtimedate(pb) < 0))
        !           505:                return (-1);
        !           506:
        !           507:        /*
        !           508:         * Now add our new revision
        !           509:         */
        !           510:        if (rcs_rev_add(pb->file,
        !           511:            (pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev),
        !           512:            pb->rcs_msg, pb->date, pb->username) != 0) {
        !           513:                cvs_log(LP_ERR, "failed to add new revision");
        !           514:                return (-1);
        !           515:        }
        !           516:
        !           517:        /*
        !           518:         * If we are checking in to a non-default (ie user-specified)
        !           519:         * revision, set head to this revision.
        !           520:         */
        !           521:        if (pb->newrev != NULL)
        !           522:                rcs_head_set(pb->file, pb->newrev);
        !           523:        else
        !           524:                pb->newrev = pb->file->rf_head;
        !           525:
        !           526:        /*
        !           527:         * New head revision has to contain entire file;
        !           528:         */
        !           529:
        !           530:         if (rcs_deltatext_set(pb->file, pb->frev, filec) == -1) {
        !           531:                cvs_log(LP_ERR, "failed to set new head revision");
        !           532:                exit(1);
        !           533:        }
        !           534:
        !           535:        /*
        !           536:         * Attach a symbolic name to this revision if specified.
        !           537:         */
        !           538:        if (pb->symbol != NULL
        !           539:            && (checkin_attach_symbol(pb) < 0))
        !           540:                return (-1);
        !           541:
        !           542:        /*
        !           543:         * Set the state of this revision if specified.
        !           544:         */
        !           545:        if (pb->state != NULL)
        !           546:                (void)rcs_state_set(pb->file, pb->newrev, pb->state);
        !           547:
        !           548:        free(pb->deltatext);
        !           549:        free(filec);
        !           550:        (void)unlink(pb->filename);
        !           551:
        !           552:        /*
        !           553:         * Do checkout if -u or -l are specified.
        !           554:         */
        !           555:        if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
        !           556:            && !(pb->flags & CI_DEFAULT))
        !           557:                checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags,
        !           558:                    pb->username);
        !           559:
        !           560:        /* File will NOW be synced */
        !           561:        rcs_close(pb->file);
        !           562:
        !           563:        if (pb->flags & INTERACTIVE) {
        !           564:                free(pb->rcs_msg);
        !           565:                pb->rcs_msg = NULL;
        !           566:        }
        !           567:        return (0);
        !           568: }
        !           569:
        !           570: /*
1.58      niallo    571:  * checkin_init()
                    572:  *
                    573:  * Does an initial check in, just enough to create the new ,v file
1.60      niallo    574:  * XXX not fully implemented yet.
1.63    ! niallo    575:  * On success, return 0. On error return -1.
1.58      niallo    576:  */
1.63    ! niallo    577: static int
1.58      niallo    578: checkin_init(struct checkin_params *pb)
                    579: {
                    580:        BUF *bp;
                    581:        char *rcs_desc, *filec;
                    582:
                    583:        /*
                    584:         * Load file contents
                    585:         */
                    586:        if ((bp = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
                    587:                cvs_log(LP_ERR, "failed to load '%s'", pb->filename);
1.63    ! niallo    588:                return (-1);
1.58      niallo    589:        }
                    590:
                    591:        if (cvs_buf_putc(bp, '\0') < 0)
1.63    ! niallo    592:                return (-1);
1.58      niallo    593:
                    594:        filec = (char *)cvs_buf_release(bp);
                    595:
                    596:        /*
                    597:         * Get description from user
                    598:         */
                    599:        rcs_desc = checkin_getdesc();
                    600:        rcs_desc_set(pb->file, rcs_desc);
1.63    ! niallo    601:        printf("set description\n");
1.58      niallo    602:
                    603:        /*
                    604:         * Now add our new revision
                    605:         */
                    606:        if (rcs_rev_add(pb->file, RCS_HEAD_REV, LOG_INIT,
1.63    ! niallo    607:            -1, pb->username) != 0) {
1.58      niallo    608:                cvs_log(LP_ERR, "failed to add new revision");
1.63    ! niallo    609:                return (-1);
        !           610:        }
        !           611:        printf("added rev\n");
        !           612:        /*
        !           613:         * If we are checking in to a non-default (ie user-specified)
        !           614:         * revision, set head to this revision.
        !           615:         */
        !           616:        if (pb->newrev != NULL)
        !           617:                rcs_head_set(pb->file, pb->newrev);
        !           618:        else
        !           619:                pb->newrev = pb->file->rf_head;
        !           620:        printf("set head rev\n");
        !           621:
        !           622:        /*
        !           623:         * New head revision has to contain entire file;
        !           624:         */
        !           625:        if (rcs_deltatext_set(pb->file, pb->frev, filec) == -1) {
        !           626:                cvs_log(LP_ERR, "failed to set new head revision");
        !           627:                return (-1);
1.58      niallo    628:        }
1.63    ! niallo    629:        printf("set delta text\n");
        !           630:
        !           631:        return (0);
1.58      niallo    632: }
                    633:
1.59      niallo    634: /*
                    635:  * checkin_attach_symbol()
                    636:  *
                    637:  * Attempt to attach the specified symbol to the revision.
                    638:  * On success, return 0. On error return -1.
                    639:  */
1.58      niallo    640: static int
                    641: checkin_attach_symbol(struct checkin_params *pb)
                    642: {
                    643:        char rbuf[16];
                    644:        int ret;
                    645:        if (verbose == 1)
                    646:                printf("symbol: %s\n", pb->symbol);
                    647:        if (pb->flags & CI_SYMFORCE)
                    648:                rcs_sym_remove(pb->file, pb->symbol);
                    649:        if ((ret = rcs_sym_add(pb->file, pb->symbol, pb->newrev) == -1)
                    650:            && (rcs_errno == RCS_ERR_DUPENT)) {
                    651:                rcsnum_tostr(rcs_sym_getrev(pb->file, pb->symbol),
                    652:                    rbuf, sizeof(rbuf));
                    653:                cvs_log(LP_ERR,
                    654:                    "symbolic name %s already bound to %s",
                    655:                    pb->symbol, rbuf);
                    656:                rcs_close(pb->file);
                    657:                return (-1);
                    658:        } else if (ret == -1) {
                    659:                cvs_log(LP_ERR, "problem adding symbol: %s",
                    660:                    pb->symbol);
                    661:                rcs_close(pb->file);
                    662:                return (-1);
                    663:        }
                    664:        return (0);
                    665: }
                    666:
1.59      niallo    667: /*
                    668:  * checkin_revert()
                    669:  *
                    670:  * If there are no differences between the working file and the latest revision
                    671:  * and the -f flag is not specified, simply revert to the latest version and
                    672:  * warn the user.
                    673:  *
                    674:  */
1.58      niallo    675: static void
                    676: checkin_revert(struct checkin_params *pb)
                    677: {
                    678:        char rbuf[16];
                    679:
                    680:        rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
                    681:        cvs_log(LP_WARN,
                    682:            "file is unchanged; reverting to previous revision %s",
                    683:            rbuf);
                    684:        (void)unlink(pb->filename);
                    685:        if ((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
                    686:                checkout_rev(pb->file, pb->frev, pb->filename,
                    687:                    pb->flags, pb->username);
                    688:        rcs_lock_remove(pb->file, pb->frev);
                    689:        rcs_close(pb->file);
                    690:        if (verbose == 1)
                    691:                printf("done\n");
                    692: }
                    693:
1.59      niallo    694: /*
                    695:  * checkin_checklock()
                    696:  *
                    697:  * Check for the existence of a lock on the file.  If there are no locks, or it
                    698:  * is not locked by the correct user, return -1.  Otherwise, return 0.
                    699:  */
1.58      niallo    700: static int
                    701: checkin_checklock(struct checkin_params *pb)
                    702: {
                    703:        int found = 0, notlocked = 1;
                    704:        struct rcs_lock *lkp;
                    705:
                    706:        if (!TAILQ_EMPTY(&(pb->file->rf_locks))) {
                    707:                TAILQ_FOREACH(lkp, &(pb->file->rf_locks), rl_list) {
                    708:                        if (!strcmp(lkp->rl_name, pb->username))
                    709:                                notlocked = 0;
                    710:
                    711:                        if (!strcmp(lkp->rl_name, pb->username) &&
                    712:                            !rcsnum_cmp(lkp->rl_num, pb->frev, 0)) {
                    713:                                found = 1;
                    714:                                return (0);
                    715:                        }
                    716:                }
                    717:        }
1.32      joris     718:
1.58      niallo    719:        if ((found == 0) && (notlocked == 0)) {
                    720:                cvs_log(LP_ERR, "no locks set for '%s'", pb->username);
                    721:                rcs_close(pb->file);
                    722:                return (-1);
                    723:        }
1.61      niallo    724:        return (0);
                    725: }
                    726:
                    727: /*
1.62      niallo    728:  * checkin_mtimedate()
1.61      niallo    729:  *
                    730:  * Set the date of the revision to be the last modification
1.62      niallo    731:  * time of the working file.
1.61      niallo    732:  *
                    733:  * On success, return 0. On error return -1.
                    734:  */
                    735: static int
1.62      niallo    736: checkin_mtimedate(struct checkin_params *pb)
1.61      niallo    737: {
                    738:        struct stat sb;
                    739:        if (stat(pb->filename, &sb) != 0) {
                    740:                cvs_log(LP_ERRNO, "failed to stat: `%s'",
                    741:                    pb->filename);
                    742:                rcs_close(pb->file);
                    743:                return (-1);
                    744:        }
                    745:        pb->date = (time_t)sb.st_mtimespec.tv_sec;
1.58      niallo    746:        return (0);
1.63    ! niallo    747: }
        !           748:
        !           749: /*
        !           750:  * checkin_rcsfile()
        !           751:  *
        !           752:  * Given a relative filename, decide where the corresponding ,v file
        !           753:  * should be.
        !           754:  *
        !           755:  * Returns pointer to a char array on success, NULL on failure.
        !           756:  */
        !           757: static char *
        !           758: checkin_choose_rcsfile(const char *filename)
        !           759: {
        !           760:        char fullpath[MAXPATHLEN], *basepath;
        !           761:        size_t len;
        !           762:        struct stat sb;
        !           763:
        !           764:        if (realpath(filename, fullpath) == NULL) {
        !           765:                cvs_log(LP_ERRNO, "realpath failed: `%s'", filename);
        !           766:                return (NULL);
        !           767:        }
        !           768:        len = strlen(fullpath);
        !           769:        while (fullpath[len] != '/')
        !           770:                len--;
        !           771:        if (len > 0) {
        !           772:                /*
        !           773:                 * Need two bytes extra for trailing slash and
        !           774:                 * NUL-termination.
        !           775:                 */
        !           776:                len += 2;
        !           777:                if ((basepath = malloc(MAXPATHLEN)) == NULL) {
        !           778:                        cvs_log(LP_ERRNO, "could not allocate memory");
        !           779:                        return (NULL);
        !           780:                }
        !           781:                strlcpy(basepath, fullpath, len);
        !           782:                strlcat(basepath, RCSDIR"/", MAXPATHLEN);
        !           783:                if ((stat(basepath, &sb) == 0) && (sb.st_mode & S_IFDIR)) {
        !           784:                        /* <path>/RCS/<filename>,v */
        !           785:                        strlcat(basepath, filename, MAXPATHLEN);
        !           786:                        strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
        !           787:                } else {
        !           788:                        /* <path>/<filename>,v */
        !           789:                        bzero(basepath, MAXPATHLEN);
        !           790:                        strlcpy(basepath, fullpath, len);
        !           791:                        strlcat(basepath, filename, MAXPATHLEN);
        !           792:                        strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
        !           793:                }
        !           794:                return (basepath);
        !           795:        } else
        !           796:                return (NULL);
1.1       niallo    797: }