[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.52

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