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

1.1       jfb         1: /*     $OpenBSD$       */
                      2: /*
                      3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@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 conditions
                      8:  * are met:
                      9:  *
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions 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/stat.h>
                     29:
                     30: #include <stdio.h>
                     31: #include <fcntl.h>
                     32: #include <stdlib.h>
                     33: #include <unistd.h>
                     34: #include <string.h>
                     35:
                     36: #include "log.h"
                     37: #include "cvs.h"
                     38:
                     39:
                     40: #define CVS_ENTRIES_NFIELDS  5
                     41: #define CVS_ENTRIES_DELIM   '/'
                     42:
                     43:
1.3       jfb        44:
                     45: static struct cvs_ent*  cvs_ent_clone (const struct cvs_ent *);
                     46:
                     47:
                     48:
1.1       jfb        49: /*
                     50:  * cvs_ent_open()
                     51:  *
                     52:  * Open the CVS Entries file for the directory <dir>.
                     53:  * Returns a pointer to the CVSENTRIES file structure on success, or NULL
                     54:  * on failure.
                     55:  */
                     56:
                     57: CVSENTRIES*
1.2       jfb        58: cvs_ent_open(const char *dir, int flags)
1.1       jfb        59: {
                     60:        size_t len;
1.3       jfb        61:        char entpath[MAXPATHLEN], ebuf[128], mode[4];
1.1       jfb        62:        FILE *fp;
1.8       jfb        63:        struct stat st;
1.1       jfb        64:        struct cvs_ent *ent;
                     65:        CVSENTRIES *ep;
                     66:
1.3       jfb        67:        memset(mode, 0, sizeof(mode));
1.8       jfb        68:
                     69:        snprintf(entpath, sizeof(entpath), "%s/" CVS_PATH_ENTRIES, dir);
                     70:
1.4       jfb        71:        switch (flags & O_ACCMODE) {
1.8       jfb        72:        case O_WRONLY:
1.4       jfb        73:        case O_RDWR:
1.8       jfb        74:                /* we have to use append otherwise the file gets truncated */
                     75:                mode[0] = 'w';
1.4       jfb        76:                mode[1] = '+';
1.8       jfb        77:                break;
1.4       jfb        78:        case O_RDONLY:
1.3       jfb        79:                mode[0] = 'r';
1.4       jfb        80:                break;
1.3       jfb        81:        }
                     82:
1.8       jfb        83:        /* we can use 'r' if the file already exists */
                     84:        if (stat(entpath, &st) == 0)
                     85:                mode[0] = 'r';
                     86:
                     87:
1.3       jfb        88:        fp = fopen(entpath, mode);
1.1       jfb        89:        if (fp == NULL) {
1.8       jfb        90:                cvs_log(LP_ERRNO, "cannot open %s for %s", entpath,
                     91:                    mode[1] == '+' ? "writing" : "reading");
1.1       jfb        92:                return (NULL);
                     93:        }
                     94:
                     95:        ep = (CVSENTRIES *)malloc(sizeof(CVSENTRIES));
                     96:        if (ep == NULL) {
                     97:                cvs_log(LP_ERRNO, "failed to allocate Entries data");
                     98:                (void)fclose(fp);
                     99:                return (NULL);
                    100:        }
1.5       jfb       101:        memset(ep, 0, sizeof(*ep));
                    102:
1.1       jfb       103:        ep->cef_path = strdup(dir);
                    104:        if (ep->cef_path == NULL) {
                    105:                cvs_log(LP_ERRNO, "failed to copy Entries path");
                    106:                free(ep);
                    107:                (void)fclose(fp);
                    108:                return (NULL);
                    109:        }
                    110:
1.4       jfb       111:        ep->cef_cur = NULL;
                    112:        TAILQ_INIT(&(ep->cef_ent));
1.3       jfb       113:
1.8       jfb       114:        rewind(fp);
                    115:
1.1       jfb       116:        while (fgets(ebuf, sizeof(ebuf), fp) != NULL) {
                    117:                len = strlen(ebuf);
                    118:                if ((len > 0) && (ebuf[len - 1] == '\n'))
                    119:                        ebuf[--len] = '\0';
1.7       jfb       120:                if (strcmp(ebuf, "D") == 0)
                    121:                        break;
1.1       jfb       122:                ent = cvs_ent_parse(ebuf);
                    123:                if (ent == NULL)
                    124:                        continue;
                    125:
1.4       jfb       126:                TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list);
1.1       jfb       127:        }
                    128:
1.4       jfb       129:        /* only keep a pointer to the open file if we're in writing mode */
1.8       jfb       130:        if ((flags & O_WRONLY) || (flags & O_RDWR)) {
                    131:                ep->cef_flags |= CVS_ENTF_WR;
1.4       jfb       132:                ep->cef_file = fp;
1.8       jfb       133:        }
1.4       jfb       134:        else
                    135:                (void)fclose(fp);
                    136:
1.8       jfb       137:        ep->cef_flags |= CVS_ENTF_SYNC;
1.1       jfb       138:        return (ep);
                    139: }
                    140:
                    141:
                    142: /*
                    143:  * cvs_ent_close()
                    144:  *
1.5       jfb       145:  * Close the Entries file <ep> and free all data.  Any reference to entries
                    146:  * structure within that file become invalid.
1.1       jfb       147:  */
                    148:
                    149: void
                    150: cvs_ent_close(CVSENTRIES *ep)
                    151: {
1.5       jfb       152:        struct cvs_ent *ent;
                    153:
1.8       jfb       154:        if ((ep->cef_flags & CVS_ENTF_WR) &&
                    155:            !(ep->cef_flags & CVS_ENTF_SYNC)) {
                    156:                /* implicit sync with disk */
                    157:                (void)cvs_ent_write(ep);
                    158:        }
                    159:
1.5       jfb       160:        if (ep->cef_file != NULL)
1.6       jfb       161:                (void)fclose(ep->cef_file);
1.5       jfb       162:        if (ep->cef_path != NULL)
                    163:                free(ep->cef_path);
                    164:
                    165:        while (!TAILQ_EMPTY(&(ep->cef_ent))) {
                    166:                ent = TAILQ_FIRST(&(ep->cef_ent));
                    167:                TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list);
                    168:                cvs_ent_free(ent);
                    169:        }
                    170:
1.1       jfb       171:        free(ep);
                    172: }
                    173:
                    174:
                    175: /*
                    176:  * cvs_ent_add()
                    177:  *
1.8       jfb       178:  * Add the entry <ent> to the Entries file <ef>.  The disk contents are not
                    179:  * modified until a call to cvs_ent_write() is performed.  This is done
                    180:  * implicitly on a call to cvs_ent_close() on an Entries file that has been
                    181:  * opened for writing.
1.7       jfb       182:  * Returns 0 on success, or -1 on failure.
1.1       jfb       183:  */
                    184:
                    185: int
                    186: cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent)
                    187: {
1.3       jfb       188:        if (ef->cef_file == NULL) {
                    189:                cvs_log(LP_ERR, "Entries file is opened in read-only mode");
                    190:                return (-1);
                    191:        }
1.1       jfb       192:
                    193:        if (cvs_ent_get(ef, ent->ce_name) != NULL)
                    194:                return (-1);
                    195:
1.8       jfb       196:        TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
                    197:
                    198:        ef->cef_flags &= ~CVS_ENTF_SYNC;
1.3       jfb       199:
                    200:        return (0);
                    201: }
                    202:
                    203:
                    204: /*
                    205:  * cvs_ent_addln()
                    206:  *
                    207:  * Add a line to the Entries file.
                    208:  */
                    209:
                    210: int
                    211: cvs_ent_addln(CVSENTRIES *ef, const char *line)
                    212: {
                    213:        struct cvs_ent *ent;
                    214:
                    215:        if (ef->cef_file == NULL) {
                    216:                cvs_log(LP_ERR, "Entries file is opened in read-only mode");
                    217:                return (-1);
                    218:        }
                    219:
                    220:        ent = cvs_ent_parse(line);
                    221:        if (ent == NULL)
                    222:                return (-1);
                    223:
                    224:        if (cvs_ent_get(ef, ent->ce_name) != NULL)
                    225:                return (-1);
1.1       jfb       226:
1.4       jfb       227:        TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list);
1.8       jfb       228:        ef->cef_flags &= ~CVS_ENTF_SYNC;
                    229:
1.1       jfb       230:        return (0);
                    231: }
                    232:
                    233:
                    234: /*
1.9     ! jfb       235:  * cvs_ent_remove()
        !           236:  *
        !           237:  * Remove an entry from the Entries file <ef>.  The entry's name is given
        !           238:  * by <name>.
        !           239:  */
        !           240:
        !           241: int
        !           242: cvs_ent_remove(CVSENTRIES *ef, const char *name)
        !           243: {
        !           244:        struct cvs_ent *ent;
        !           245:
        !           246:        ent = cvs_ent_get(ef, name);
        !           247:        if (ent == NULL)
        !           248:                return (-1);
        !           249:
        !           250:        TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list);
        !           251:        cvs_ent_free(ent);
        !           252:
        !           253:        ef->cef_flags &= ~CVS_ENTF_SYNC;
        !           254:
        !           255:        return (0);
        !           256: }
        !           257:
        !           258:
        !           259: /*
1.1       jfb       260:  * cvs_ent_get()
                    261:  *
                    262:  * Get the CVS entry from the Entries file <ef> whose 'name' portion matches
                    263:  * <file>.
                    264:  * Returns a pointer to the cvs entry structure on success, or NULL on failure.
                    265:  */
                    266:
                    267: struct cvs_ent*
                    268: cvs_ent_get(CVSENTRIES *ef, const char *file)
                    269: {
1.4       jfb       270:        struct cvs_ent *ep;
1.1       jfb       271:
1.4       jfb       272:        TAILQ_FOREACH(ep, &(ef->cef_ent), ce_list)
                    273:                if (strcmp(ep->ce_name, file) == 0)
                    274:                        return (ep);
1.1       jfb       275:
                    276:        return (NULL);
                    277: }
                    278:
                    279:
                    280: /*
                    281:  * cvs_ent_next()
                    282:  *
1.4       jfb       283:  * This function is used to iterate over the entries in an Entries file.  The
                    284:  * first call will return the first entry of the file and each subsequent call
                    285:  * will return the entry following the last one returned.
1.1       jfb       286:  * Returns a pointer to the cvs entry structure on success, or NULL on failure.
                    287:  */
                    288:
                    289: struct cvs_ent*
                    290: cvs_ent_next(CVSENTRIES *ef)
                    291: {
1.5       jfb       292:        if (ef->cef_cur == NULL)
1.4       jfb       293:                ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent));
1.5       jfb       294:        else
                    295:                ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
                    296:        return (ef->cef_cur);
1.1       jfb       297: }
                    298:
                    299:
                    300: /*
                    301:  * cvs_ent_parse()
                    302:  *
                    303:  * Parse a single line from a CVS/Entries file and return a cvs_entry structure
                    304:  * containing all the parsed information.
                    305:  */
                    306:
                    307: struct cvs_ent*
                    308: cvs_ent_parse(const char *entry)
                    309: {
                    310:        int i;
                    311:        char *fields[CVS_ENTRIES_NFIELDS], *sp, *dp;
                    312:        struct cvs_ent *entp;
                    313:
                    314:        entp = (struct cvs_ent *)malloc(sizeof(*entp));
                    315:        if (entp == NULL) {
                    316:                cvs_log(LP_ERRNO, "failed to allocate CVS entry");
                    317:                return (NULL);
                    318:        }
1.5       jfb       319:        memset(entp, 0, sizeof(*entp));
1.1       jfb       320:
                    321:        entp->ce_rev = rcsnum_alloc();
                    322:        if (entp->ce_rev == NULL) {
                    323:                free(entp);
                    324:                return (NULL);
                    325:        }
                    326:
                    327:        entp->ce_line = strdup(entry);
                    328:        if (entp->ce_line == NULL) {
1.5       jfb       329:                cvs_ent_free(entp);
1.1       jfb       330:                return (NULL);
                    331:        }
                    332:
                    333:        entp->ce_buf = strdup(entry);
                    334:        if (entp->ce_buf == NULL) {
1.5       jfb       335:                cvs_ent_free(entp);
1.1       jfb       336:                return (NULL);
                    337:        }
                    338:        sp = entp->ce_buf;
                    339:
                    340:        if (*sp == CVS_ENTRIES_DELIM)
                    341:                entp->ce_type = CVS_ENT_FILE;
                    342:        else if (*sp == 'D') {
                    343:                entp->ce_type = CVS_ENT_DIR;
                    344:                sp++;
                    345:        }
                    346:        else {
                    347:                /* unknown entry, ignore for future expansion */
                    348:                entp->ce_type = CVS_ENT_NONE;
                    349:                sp++;
                    350:        }
                    351:
                    352:        sp++;
                    353:        i = 0;
                    354:        do {
                    355:                dp = strchr(sp, CVS_ENTRIES_DELIM);
                    356:                if (dp != NULL)
                    357:                        *(dp++) = '\0';
                    358:                fields[i++] = sp;
                    359:                sp = dp;
                    360:        } while ((dp != NULL) && (i < CVS_ENTRIES_NFIELDS));
                    361:
                    362:        entp->ce_name = fields[0];
                    363:
                    364:        if (entp->ce_type == CVS_ENT_FILE) {
                    365:                rcsnum_aton(fields[1], NULL, entp->ce_rev);
                    366:                entp->ce_timestamp = fields[2];
                    367:                entp->ce_opts = fields[3];
                    368:                entp->ce_tag = fields[4];
                    369:        }
                    370:
                    371:        return (entp);
1.5       jfb       372: }
                    373:
                    374:
                    375: /*
                    376:  * cvs_ent_free()
                    377:  *
                    378:  * Free a single CVS entries structure.
                    379:  */
                    380:
                    381: void
                    382: cvs_ent_free(struct cvs_ent *ent)
                    383: {
                    384:        if (ent->ce_rev != NULL)
                    385:                rcsnum_free(ent->ce_rev);
                    386:        if (ent->ce_line != NULL)
                    387:                free(ent->ce_line);
                    388:        if (ent->ce_buf != NULL)
                    389:                free(ent->ce_buf);
                    390:        free(ent);
                    391: }
                    392:
                    393:
                    394: /*
                    395:  * cvs_ent_getent()
                    396:  *
                    397:  * Get a single entry from the CVS/Entries file of the basename portion of
                    398:  * path <path> and return that entry.  That entry must later be freed using
                    399:  * cvs_ent_free().
                    400:  */
                    401:
                    402: struct cvs_ent*
                    403: cvs_ent_getent(const char *path)
                    404: {
                    405:        char base[MAXPATHLEN], file[MAXPATHLEN];
                    406:        CVSENTRIES *entf;
                    407:        struct cvs_ent *ep;
                    408:
                    409:        cvs_splitpath(path, base, sizeof(base), file, sizeof(file));
                    410:
                    411:        entf = cvs_ent_open(base, O_RDONLY);
                    412:        if (entf == NULL)
                    413:                return (NULL);
                    414:
                    415:        ep = cvs_ent_get(entf, file);
                    416:        if (ep != NULL) {
                    417:                /* take it out of the queue so it doesn't get freed */
                    418:                TAILQ_REMOVE(&(entf->cef_ent), ep, ce_list);
                    419:        }
                    420:
                    421:        cvs_ent_close(entf);
                    422:        return (ep);
1.8       jfb       423: }
                    424:
                    425:
                    426: /*
                    427:  * cvs_ent_write()
                    428:  *
                    429:  * Explicitly write the contents of the Entries file <ef> to disk.
                    430:  * Returns 0 on success, or -1 on failure.
                    431:  */
                    432:
                    433: int
                    434: cvs_ent_write(CVSENTRIES *ef)
                    435: {
                    436:        char revbuf[64];
                    437:        struct cvs_ent *ent;
                    438:
                    439:        if (ef->cef_file == NULL)
                    440:                return (-1);
                    441:
                    442:        if (ef->cef_flags & CVS_ENTF_SYNC)
                    443:                return (0);
                    444:
                    445:        /* reposition ourself at beginning of file */
                    446:        rewind(ef->cef_file);
                    447:        TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) {
                    448:                if (ent->ce_type == CVS_ENT_DIR)
                    449:                        putc('D', ef->cef_file);
                    450:
                    451:                rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf));
                    452:                fprintf(ef->cef_file, "/%s/%s/%s/%s/%s\n", ent->ce_name,
                    453:                    revbuf, ent->ce_timestamp, "", "");
                    454:        }
                    455:
                    456:        /* terminating line */
                    457:        fprintf(ef->cef_file, "D\n");
                    458:
                    459:        ef->cef_flags |= CVS_ENTF_SYNC;
                    460:
                    461:        return (0);
1.1       jfb       462: }