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

Annotation of src/usr.bin/cvs/entries.c, Revision 1.56

1.56    ! deraadt     1: /*     $OpenBSD: entries.c,v 1.55 2006/04/05 01:38:55 ray Exp $        */
1.1       jfb         2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.20      tedu        4:  * All rights reserved.
1.1       jfb         5:  *
1.20      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.20      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.20      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.20      tedu       24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1       jfb        25:  */
                     26:
1.54      xsa        27: #include "includes.h"
1.1       jfb        28:
1.34      xsa        29: #include "cvs.h"
1.1       jfb        30: #include "log.h"
                     31:
                     32:
1.42      xsa        33: #define CVS_ENTRIES_NFIELDS    6
                     34: #define CVS_ENTRIES_DELIM      '/'
1.1       jfb        35:
                     36:
                     37: /*
                     38:  * cvs_ent_open()
                     39:  *
                     40:  * Open the CVS Entries file for the directory <dir>.
                     41:  * Returns a pointer to the CVSENTRIES file structure on success, or NULL
                     42:  * on failure.
                     43:  */
1.42      xsa        44: CVSENTRIES *
1.2       jfb        45: cvs_ent_open(const char *dir, int flags)
1.1       jfb        46: {
                     47:        size_t len;
1.36      xsa        48:        int exists, nodir;
1.52      joris      49:        char bpath[MAXPATHLEN], *p;
1.45      xsa        50:        char cdpath[MAXPATHLEN], ebuf[CVS_ENT_MAXLINELEN], entpath[MAXPATHLEN];
                     51:        char mode[4];
1.1       jfb        52:        FILE *fp;
1.8       jfb        53:        struct stat st;
1.1       jfb        54:        struct cvs_ent *ent;
                     55:        CVSENTRIES *ep;
                     56:
1.12      jfb        57:        exists = 0;
1.36      xsa        58:        nodir = 1;
1.3       jfb        59:        memset(mode, 0, sizeof(mode));
1.8       jfb        60:
1.36      xsa        61:        /*
                     62:         * Check if the CVS/ dir does exist. If it does,
                     63:         * maybe the Entries file was deleted by accident,
                     64:         * display error message. Else we might be doing a fresh
                     65:         * update or checkout of a module.
                     66:         */
                     67:        len = cvs_path_cat(dir, CVS_PATH_CVSDIR, cdpath, sizeof(cdpath));
1.37      xsa        68:        if (len >= sizeof(cdpath))
1.36      xsa        69:                return (NULL);
1.37      xsa        70:
1.56    ! deraadt    71:        if (stat(cdpath, &st) == 0 && S_ISDIR(st.st_mode))
1.36      xsa        72:                nodir = 0;      /* the CVS/ directory does exist */
                     73:
1.52      joris      74:        len = cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, bpath, sizeof(bpath));
                     75:        if (len >= sizeof(entpath))
                     76:                return (NULL);
                     77:
1.32      jfb        78:        len = cvs_path_cat(dir, CVS_PATH_ENTRIES, entpath, sizeof(entpath));
1.35      joris      79:        if (len >= sizeof(entpath))
1.26      xsa        80:                return (NULL);
1.8       jfb        81:
1.4       jfb        82:        switch (flags & O_ACCMODE) {
1.8       jfb        83:        case O_WRONLY:
1.4       jfb        84:        case O_RDWR:
1.8       jfb        85:                /* we have to use append otherwise the file gets truncated */
                     86:                mode[0] = 'w';
1.4       jfb        87:                mode[1] = '+';
1.8       jfb        88:                break;
1.4       jfb        89:        case O_RDONLY:
1.3       jfb        90:                mode[0] = 'r';
1.4       jfb        91:                break;
1.3       jfb        92:        }
                     93:
1.8       jfb        94:        /* we can use 'r' if the file already exists */
1.12      jfb        95:        if (stat(entpath, &st) == 0) {
                     96:                exists = 1;
1.8       jfb        97:                mode[0] = 'r';
1.12      jfb        98:        }
1.8       jfb        99:
1.3       jfb       100:        fp = fopen(entpath, mode);
1.1       jfb       101:        if (fp == NULL) {
1.44      xsa       102:                if (nodir == 0)
1.36      xsa       103:                        cvs_log(LP_ERRNO, "cannot open %s for %s", entpath,
                    104:                            mode[1] == '+' ? "writing" : "reading");
1.1       jfb       105:                return (NULL);
                    106:        }
                    107:
1.55      ray       108:        ep = xcalloc(1, sizeof(*ep));
1.5       jfb       109:
1.53      joris     110:        ep->cef_path = xstrdup(entpath);
                    111:        ep->cef_bpath = xstrdup(bpath);
1.4       jfb       112:        ep->cef_cur = NULL;
                    113:        TAILQ_INIT(&(ep->cef_ent));
1.3       jfb       114:
1.43      xsa       115:        while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) {
1.1       jfb       116:                len = strlen(ebuf);
1.56    ! deraadt   117:                if (len > 0 && ebuf[len - 1] == '\n')
1.1       jfb       118:                        ebuf[--len] = '\0';
1.56    ! deraadt   119:                if (ebuf[0] == 'D' && ebuf[1] == '\0')
1.7       jfb       120:                        break;
1.1       jfb       121:                ent = cvs_ent_parse(ebuf);
                    122:                if (ent == NULL)
                    123:                        continue;
                    124:
1.4       jfb       125:                TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list);
1.1       jfb       126:        }
1.52      joris     127:
1.12      jfb       128:        if (ferror(fp)) {
1.18      krapht    129:                cvs_log(LP_ERRNO, "read error on %s", entpath);
1.25      jfb       130:                (void)fclose(fp);
1.12      jfb       131:                cvs_ent_close(ep);
                    132:                return (NULL);
                    133:        }
1.1       jfb       134:
1.4       jfb       135:        /* only keep a pointer to the open file if we're in writing mode */
1.16      jfb       136:        if ((flags & O_WRONLY) || (flags & O_RDWR))
1.8       jfb       137:                ep->cef_flags |= CVS_ENTF_WR;
1.16      jfb       138:
                    139:        (void)fclose(fp);
1.4       jfb       140:
1.52      joris     141:        /*
                    142:         * look for Entries.Log and add merge it together with our
                    143:         * list of things.
                    144:         */
                    145:        len = cvs_path_cat(dir, CVS_PATH_LOGENTRIES, entpath, sizeof(entpath));
                    146:        if (len >= sizeof(entpath)) {
                    147:                cvs_ent_close(ep);
                    148:                return (NULL);
                    149:        }
                    150:
                    151:        fp = fopen(entpath, "r");
                    152:        if (fp != NULL) {
                    153:                while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) {
                    154:                        len = strlen(ebuf);
1.56    ! deraadt   155:                        if (len > 0 && ebuf[len - 1] == '\n')
1.52      joris     156:                                ebuf[--len] = '\0';
                    157:
                    158:                        p = &ebuf[2];
                    159:                        ent = cvs_ent_parse(p);
                    160:                        if (ent == NULL)
                    161:                                continue;
                    162:
                    163:                        if (ebuf[0] == 'A')
                    164:                                cvs_ent_add(ep, ent);
                    165:                        else if (ebuf[0] == 'R')
                    166:                                cvs_ent_remove(ep, ent->ce_name, 0);
                    167:                }
                    168:                (void)fclose(fp);
                    169:
                    170:                /* always un-synced here, because we
                    171:                 * just added or removed entries.
                    172:                 */
                    173:                ep->cef_flags &= ~CVS_ENTF_SYNC;
                    174:        } else {
                    175:                if (exists == 1)
                    176:                        ep->cef_flags |= CVS_ENTF_SYNC;
                    177:        }
1.12      jfb       178:
1.1       jfb       179:        return (ep);
                    180: }
                    181:
                    182:
                    183: /*
                    184:  * cvs_ent_close()
                    185:  *
1.5       jfb       186:  * Close the Entries file <ep> and free all data.  Any reference to entries
                    187:  * structure within that file become invalid.
1.1       jfb       188:  */
                    189: void
                    190: cvs_ent_close(CVSENTRIES *ep)
                    191: {
1.5       jfb       192:        struct cvs_ent *ent;
                    193:
1.56    ! deraadt   194:        if (cvs_noexec == 0 && (ep->cef_flags & CVS_ENTF_WR) &&
1.8       jfb       195:            !(ep->cef_flags & CVS_ENTF_SYNC)) {
                    196:                /* implicit sync with disk */
                    197:                (void)cvs_ent_write(ep);
                    198:        }
                    199:
1.5       jfb       200:        if (ep->cef_path != NULL)
1.53      joris     201:                xfree(ep->cef_path);
1.5       jfb       202:
1.52      joris     203:        if (ep->cef_bpath != NULL)
1.53      joris     204:                xfree(ep->cef_bpath);
1.52      joris     205:
1.5       jfb       206:        while (!TAILQ_EMPTY(&(ep->cef_ent))) {
                    207:                ent = TAILQ_FIRST(&(ep->cef_ent));
                    208:                TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list);
                    209:                cvs_ent_free(ent);
                    210:        }
                    211:
1.53      joris     212:        xfree(ep);
1.1       jfb       213: }
                    214:
                    215:
                    216: /*
                    217:  * cvs_ent_add()
                    218:  *
1.8       jfb       219:  * Add the entry <ent> to the Entries file <ef>.  The disk contents are not
                    220:  * modified until a call to cvs_ent_write() is performed.  This is done
                    221:  * implicitly on a call to cvs_ent_close() on an Entries file that has been
                    222:  * opened for writing.
1.7       jfb       223:  * Returns 0 on success, or -1 on failure.
1.1       jfb       224:  */
                    225: int
                    226: cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent)
                    227: {
1.16      jfb       228:        if (!(ef->cef_flags & CVS_ENTF_WR)) {
1.3       jfb       229:                cvs_log(LP_ERR, "Entries file is opened in read-only mode");
                    230:                return (-1);
                    231:        }
1.1       jfb       232:
1.21      jfb       233:        if (cvs_ent_get(ef, ent->ce_name) != NULL) {
                    234:                cvs_log(LP_ERR, "attempt to add duplicate entry for `%s'",
                    235:                    ent->ce_name);
1.1       jfb       236:                return (-1);
1.21      jfb       237:        }
1.1       jfb       238:
1.8       jfb       239:        TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
                    240:
                    241:        ef->cef_flags &= ~CVS_ENTF_SYNC;
1.3       jfb       242:
                    243:        return (0);
                    244: }
                    245:
                    246:
                    247: /*
                    248:  * cvs_ent_addln()
                    249:  *
                    250:  * Add a line to the Entries file.
                    251:  */
                    252: int
                    253: cvs_ent_addln(CVSENTRIES *ef, const char *line)
                    254: {
                    255:        struct cvs_ent *ent;
                    256:
1.16      jfb       257:        if (!(ef->cef_flags & CVS_ENTF_WR)) {
1.3       jfb       258:                cvs_log(LP_ERR, "Entries file is opened in read-only mode");
                    259:                return (-1);
                    260:        }
                    261:
                    262:        ent = cvs_ent_parse(line);
                    263:        if (ent == NULL)
                    264:                return (-1);
                    265:
                    266:        if (cvs_ent_get(ef, ent->ce_name) != NULL)
                    267:                return (-1);
1.1       jfb       268:
1.4       jfb       269:        TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
1.8       jfb       270:        ef->cef_flags &= ~CVS_ENTF_SYNC;
                    271:
1.1       jfb       272:        return (0);
                    273: }
                    274:
                    275:
                    276: /*
1.9       jfb       277:  * cvs_ent_remove()
                    278:  *
                    279:  * Remove an entry from the Entries file <ef>.  The entry's name is given
                    280:  * by <name>.
                    281:  */
                    282: int
1.51      joris     283: cvs_ent_remove(CVSENTRIES *ef, const char *name, int useprev)
1.9       jfb       284: {
                    285:        struct cvs_ent *ent;
1.44      xsa       286:
                    287:        cvs_log(LP_TRACE, "cvs_ent_remove(%s)", name);
1.16      jfb       288:
1.9       jfb       289:        ent = cvs_ent_get(ef, name);
                    290:        if (ent == NULL)
                    291:                return (-1);
                    292:
1.22      jfb       293:        if (ef->cef_cur == ent) {
                    294:                /* if this element was the last one retrieved through a
                    295:                 * call to cvs_ent_next(), point to the next element to avoid
                    296:                 * keeping an invalid reference.
                    297:                 */
1.51      joris     298:                if (useprev) {
                    299:                        ef->cef_cur = TAILQ_PREV(ef->cef_cur,
                    300:                            cvsentrieshead, ce_list);
                    301:                } else {
                    302:                        ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
                    303:                }
1.22      jfb       304:        }
1.9       jfb       305:        TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list);
                    306:        cvs_ent_free(ent);
                    307:
                    308:        ef->cef_flags &= ~CVS_ENTF_SYNC;
                    309:
                    310:        return (0);
                    311: }
                    312:
                    313:
                    314: /*
1.1       jfb       315:  * cvs_ent_get()
                    316:  *
                    317:  * Get the CVS entry from the Entries file <ef> whose 'name' portion matches
                    318:  * <file>.
                    319:  * Returns a pointer to the cvs entry structure on success, or NULL on failure.
                    320:  */
1.42      xsa       321: struct cvs_ent *
1.1       jfb       322: cvs_ent_get(CVSENTRIES *ef, const char *file)
                    323: {
1.39      xsa       324:        struct cvs_ent *ent;
1.1       jfb       325:
1.39      xsa       326:        TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list)
                    327:                if (strcmp(ent->ce_name, file) == 0)
                    328:                        return (ent);
1.1       jfb       329:
                    330:        return (NULL);
                    331: }
                    332:
                    333:
                    334: /*
                    335:  * cvs_ent_next()
                    336:  *
1.4       jfb       337:  * This function is used to iterate over the entries in an Entries file.  The
                    338:  * first call will return the first entry of the file and each subsequent call
                    339:  * will return the entry following the last one returned.
1.1       jfb       340:  * Returns a pointer to the cvs entry structure on success, or NULL on failure.
                    341:  */
1.42      xsa       342: struct cvs_ent *
1.1       jfb       343: cvs_ent_next(CVSENTRIES *ef)
                    344: {
1.5       jfb       345:        if (ef->cef_cur == NULL)
1.4       jfb       346:                ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent));
1.5       jfb       347:        else
                    348:                ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
                    349:        return (ef->cef_cur);
1.1       jfb       350: }
                    351:
                    352:
                    353: /*
                    354:  * cvs_ent_parse()
                    355:  *
1.25      jfb       356:  * Parse a single line from a CVS/Entries file and return a cvs_ent structure
1.1       jfb       357:  * containing all the parsed information.
                    358:  */
                    359: struct cvs_ent*
                    360: cvs_ent_parse(const char *entry)
                    361: {
                    362:        int i;
1.10      jfb       363:        char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp;
1.39      xsa       364:        struct cvs_ent *ent;
1.1       jfb       365:
1.53      joris     366:        buf = xstrdup(entry);
1.10      jfb       367:        sp = buf;
                    368:        i = 0;
                    369:        do {
                    370:                dp = strchr(sp, CVS_ENTRIES_DELIM);
                    371:                if (dp != NULL)
                    372:                        *(dp++) = '\0';
                    373:                fields[i++] = sp;
                    374:                sp = dp;
1.56    ! deraadt   375:        } while (dp != NULL && i < CVS_ENTRIES_NFIELDS);
1.10      jfb       376:
                    377:        if (i < CVS_ENTRIES_NFIELDS) {
                    378:                cvs_log(LP_ERR, "missing fields in entry line `%s'", entry);
                    379:                return (NULL);
                    380:        }
                    381:
1.55      ray       382:        ent = xcalloc(1, sizeof(*ent));
1.39      xsa       383:        ent->ce_buf = buf;
1.1       jfb       384:
1.10      jfb       385:        if (*fields[0] == '\0')
1.39      xsa       386:                ent->ce_type = CVS_ENT_FILE;
1.10      jfb       387:        else if (*fields[0] == 'D')
1.39      xsa       388:                ent->ce_type = CVS_ENT_DIR;
1.10      jfb       389:        else
1.39      xsa       390:                ent->ce_type = CVS_ENT_NONE;
1.1       jfb       391:
1.39      xsa       392:        ent->ce_status = CVS_ENT_REG;
                    393:        ent->ce_name = fields[1];
1.48      joris     394:        ent->processed = 0;
1.1       jfb       395:
1.39      xsa       396:        if (ent->ce_type == CVS_ENT_FILE) {
1.24      jfb       397:                if (*fields[2] == '-') {
1.39      xsa       398:                        ent->ce_status = CVS_ENT_REMOVED;
1.24      jfb       399:                        sp = fields[2] + 1;
                    400:                } else {
                    401:                        sp = fields[2];
1.56    ! deraadt   402:                        if (fields[2][0] == '0' && fields[2][1] == '\0')
1.39      xsa       403:                                ent->ce_status = CVS_ENT_ADDED;
1.24      jfb       404:                }
1.38      joris     405:
1.39      xsa       406:                if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) {
                    407:                        cvs_ent_free(ent);
1.29      jfb       408:                        return (NULL);
                    409:                }
1.24      jfb       410:
1.38      joris     411:                if (cvs_cmdop == CVS_OP_SERVER) {
                    412:                        if (!strcmp(fields[3], "up to date"))
1.39      xsa       413:                                ent->ce_status = CVS_ENT_UPTODATE;
1.38      joris     414:                } else {
1.56    ! deraadt   415:                        if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 ||
        !           416:                            strncmp(fields[3], "Initial ", 8) == 0)
1.39      xsa       417:                                ent->ce_mtime = CVS_DATE_DMSEC;
1.38      joris     418:                        else
1.39      xsa       419:                                ent->ce_mtime = cvs_date_parse(fields[3]);
1.38      joris     420:                }
1.29      jfb       421:        }
1.24      jfb       422:
1.39      xsa       423:        ent->ce_opts = fields[4];
                    424:        ent->ce_tag = fields[5];
                    425:        return (ent);
1.5       jfb       426: }
                    427:
                    428: /*
                    429:  * cvs_ent_free()
                    430:  *
                    431:  * Free a single CVS entries structure.
                    432:  */
                    433: void
                    434: cvs_ent_free(struct cvs_ent *ent)
                    435: {
                    436:        if (ent->ce_rev != NULL)
                    437:                rcsnum_free(ent->ce_rev);
                    438:        if (ent->ce_buf != NULL)
1.53      joris     439:                xfree(ent->ce_buf);
                    440:        xfree(ent);
1.5       jfb       441: }
1.8       jfb       442:
                    443: /*
                    444:  * cvs_ent_write()
                    445:  *
                    446:  * Explicitly write the contents of the Entries file <ef> to disk.
                    447:  * Returns 0 on success, or -1 on failure.
                    448:  */
                    449: int
                    450: cvs_ent_write(CVSENTRIES *ef)
                    451: {
1.13      jfb       452:        size_t len;
                    453:        char revbuf[64], timebuf[32];
1.8       jfb       454:        struct cvs_ent *ent;
1.32      jfb       455:        FILE *fp;
1.8       jfb       456:
                    457:        if (ef->cef_flags & CVS_ENTF_SYNC)
                    458:                return (0);
                    459:
1.52      joris     460:        if ((fp = fopen(ef->cef_bpath, "w")) == NULL) {
                    461:                cvs_log(LP_ERRNO, "failed to open Entries `%s'", ef->cef_bpath);
1.32      jfb       462:                return (-1);
1.16      jfb       463:        }
                    464:
1.8       jfb       465:        TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) {
1.15      jfb       466:                if (ent->ce_type == CVS_ENT_DIR) {
1.32      jfb       467:                        putc('D', fp);
1.15      jfb       468:                        timebuf[0] = '\0';
                    469:                        revbuf[0] = '\0';
1.19      deraadt   470:                } else {
1.15      jfb       471:                        rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf));
1.56    ! deraadt   472:                        if (ent->ce_mtime == CVS_DATE_DMSEC &&
        !           473:                            ent->ce_status != CVS_ENT_ADDED)
1.15      jfb       474:                                strlcpy(timebuf, CVS_DATE_DUMMY,
                    475:                                    sizeof(timebuf));
1.47      joris     476:                        else if (ent->ce_status == CVS_ENT_ADDED) {
                    477:                                strlcpy(timebuf, "Initial ", sizeof(timebuf));
                    478:                                strlcat(timebuf, ent->ce_name, sizeof(timebuf));
                    479:                        } else {
1.15      jfb       480:                                ctime_r(&(ent->ce_mtime), timebuf);
                    481:                                len = strlen(timebuf);
1.56    ! deraadt   482:                                if (len > 0 && timebuf[len - 1] == '\n')
1.15      jfb       483:                                        timebuf[--len] = '\0';
                    484:                        }
1.38      joris     485:                }
                    486:
                    487:                if (cvs_cmdop == CVS_OP_SERVER) {
                    488:                        if (ent->ce_status == CVS_ENT_UPTODATE)
                    489:                                strlcpy(timebuf, "up to date", sizeof(timebuf));
                    490:                        else
                    491:                                timebuf[0] = '\0';
1.15      jfb       492:                }
1.8       jfb       493:
1.32      jfb       494:                fprintf(fp, "/%s/%s%s/%s/%s/%s\n", ent->ce_name,
1.27      joris     495:                    (ent->ce_status == CVS_ENT_REMOVED) ? "-" : "", revbuf,
1.49      xsa       496:                    timebuf, (ent->ce_opts != NULL) ? ent->ce_opts : "",
                    497:                    (ent->ce_tag != NULL) ? ent->ce_tag : "");
1.8       jfb       498:        }
                    499:
                    500:        /* terminating line */
1.32      jfb       501:        putc('D', fp);
                    502:        putc('\n', fp);
1.8       jfb       503:
                    504:        ef->cef_flags |= CVS_ENTF_SYNC;
1.32      jfb       505:        fclose(fp);
1.52      joris     506:
                    507:        /* rename Entries.Backup to Entries */
                    508:        cvs_rename(ef->cef_bpath, ef->cef_path);
                    509:
                    510:        /* remove Entries.Log */
                    511:        cvs_unlink(CVS_PATH_LOGENTRIES);
                    512:
1.8       jfb       513:        return (0);
1.1       jfb       514: }