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

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