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

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