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

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