[BACK]Return to tbl.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/tbl.c, Revision 1.1

1.1     ! schwarze    1: /*     $Id: tbl.c,v 1.14 2009/09/12 16:05:34 kristaps Exp $ */
        !             2: /*
        !             3:  * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #include <sys/queue.h>
        !            18:
        !            19: #include <assert.h>
        !            20: #include <errno.h>
        !            21: #include <limits.h>
        !            22: #include <stdio.h>
        !            23: #include <stdlib.h>
        !            24: #include <string.h>
        !            25:
        !            26: #include "tbl.h"
        !            27: #include "tbl_extern.h"
        !            28:
        !            29:
        !            30: const  char *const      errnames[ERR_MAX] = {
        !            31:        "bad syntax",    /* ERR_SYNTAX */
        !            32:        "bad option"     /* ERR_OPTION */
        !            33: };
        !            34:
        !            35: static char             buf[1024]; /* XXX */
        !            36:
        !            37: static enum tbl_tok     tbl_next_char(char);
        !            38: static void             tbl_init(struct tbl *);
        !            39: static void             tbl_clear(struct tbl *);
        !            40: static struct tbl_head *tbl_head_alloc(struct tbl *);
        !            41: static void             tbl_span_free(struct tbl_span *);
        !            42: static void             tbl_data_free(struct tbl_data *);
        !            43: static void             tbl_row_free(struct tbl_row *);
        !            44:
        !            45: static void             headadj(const struct tbl_cell *,
        !            46:                                struct tbl_head *);
        !            47:
        !            48: static void
        !            49: tbl_init(struct tbl *tbl)
        !            50: {
        !            51:
        !            52:        bzero(tbl, sizeof(struct tbl));
        !            53:
        !            54:        tbl->part = TBL_PART_OPTS;
        !            55:        tbl->tab = '\t';
        !            56:        tbl->linesize = 12;
        !            57:        tbl->decimal = '.';
        !            58:
        !            59:        TAILQ_INIT(&tbl->span);
        !            60:        TAILQ_INIT(&tbl->row);
        !            61:        TAILQ_INIT(&tbl->head);
        !            62: }
        !            63:
        !            64:
        !            65: int
        !            66: tbl_read(struct tbl *tbl, const char *f, int ln, const char *p, int len)
        !            67: {
        !            68:
        !            69:        if (len && TBL_PART_OPTS == tbl->part)
        !            70:                if (';' != p[len - 1])
        !            71:                        tbl->part = TBL_PART_LAYOUT;
        !            72:
        !            73:        switch (tbl->part) {
        !            74:        case (TBL_PART_OPTS):
        !            75:                return(tbl_option(tbl, f, ln, p));
        !            76:        case (TBL_PART_CLAYOUT):
        !            77:                /* FALLTHROUGH */
        !            78:        case (TBL_PART_LAYOUT):
        !            79:                return(tbl_layout(tbl, f, ln, p));
        !            80:        case (TBL_PART_DATA):
        !            81:                return(tbl_data(tbl, f, ln, p));
        !            82:        case (TBL_PART_ERROR):
        !            83:                break;
        !            84:        }
        !            85:
        !            86:        return(0);
        !            87: }
        !            88:
        !            89:
        !            90: int
        !            91: tbl_close(struct tbl *tbl, const char *f, int ln)
        !            92: {
        !            93:
        !            94:        if (TBL_PART_DATA != tbl->part)
        !            95:                return(tbl_errx(tbl, ERR_SYNTAX, f, ln, 0));
        !            96:        if ( ! tbl_data_close(tbl, f, ln))
        !            97:                return(0);
        !            98: #if 1
        !            99:        return(tbl_calc_term(tbl));
        !           100: #else
        !           101:        return(tbl_calc_tree(tbl));
        !           102: #endif
        !           103: }
        !           104:
        !           105:
        !           106: int
        !           107: tbl_write(const struct tbl *tbl)
        !           108: {
        !           109:
        !           110: #if 1
        !           111:        return(tbl_write_term(tbl));
        !           112: #else
        !           113:        return(tbl_write_tree(tbl));
        !           114: #endif
        !           115: }
        !           116:
        !           117:
        !           118: static enum tbl_tok
        !           119: tbl_next_char(char c)
        !           120: {
        !           121:
        !           122:        /*
        !           123:         * These are delimiting tokens.  They separate out words in the
        !           124:         * token stream.
        !           125:         */
        !           126:
        !           127:        switch (c) {
        !           128:        case ('('):
        !           129:                return(TBL_TOK_OPENPAREN);
        !           130:        case (')'):
        !           131:                return(TBL_TOK_CLOSEPAREN);
        !           132:        case (' '):
        !           133:                return(TBL_TOK_SPACE);
        !           134:        case ('\t'):
        !           135:                return(TBL_TOK_TAB);
        !           136:        case (';'):
        !           137:                return(TBL_TOK_SEMICOLON);
        !           138:        case ('.'):
        !           139:                return(TBL_TOK_PERIOD);
        !           140:        case (','):
        !           141:                return(TBL_TOK_COMMA);
        !           142:        case (0):
        !           143:                return(TBL_TOK_NIL);
        !           144:        default:
        !           145:                break;
        !           146:        }
        !           147:
        !           148:        return(TBL_TOK_WORD);
        !           149: }
        !           150:
        !           151:
        !           152: const char *
        !           153: tbl_last(void)
        !           154: {
        !           155:
        !           156:        return(buf);
        !           157: }
        !           158:
        !           159:
        !           160: int
        !           161: tbl_last_uint(void)
        !           162: {
        !           163:        char            *ep;
        !           164:        long             lval;
        !           165:
        !           166:        /* From OpenBSD's strtol(3).  Gross. */
        !           167:
        !           168:        errno = 0;
        !           169:        lval = strtol(buf, &ep, 10);
        !           170:        if (buf[0] == 0 || *ep != 0)
        !           171:                return(-1);
        !           172:        if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
        !           173:                return(-1);
        !           174:        if (lval < 0 || lval > INT_MAX)
        !           175:                return(-1);
        !           176:
        !           177:        return((int)lval);
        !           178: }
        !           179:
        !           180:
        !           181: enum tbl_tok
        !           182: tbl_next(const char *p, int *pos)
        !           183: {
        !           184:        int              i;
        !           185:        enum tbl_tok     c;
        !           186:
        !           187:        buf[0] = 0;
        !           188:
        !           189:        if (TBL_TOK_WORD != (c = tbl_next_char(p[*pos]))) {
        !           190:                if (TBL_TOK_NIL != c) {
        !           191:                        buf[0] = p[*pos];
        !           192:                        buf[1] = 0;
        !           193:                        (*pos)++;
        !           194:                }
        !           195:                return(c);
        !           196:        }
        !           197:
        !           198:        /*
        !           199:         * Copy words into a nil-terminated buffer.  For now, we use a
        !           200:         * static buffer.  Eventually this should be made into a dynamic
        !           201:         * one living in struct tbl.
        !           202:         */
        !           203:
        !           204:        for (i = 0; i < 1023; i++, (*pos)++)
        !           205:                if (TBL_TOK_WORD == tbl_next_char(p[*pos]))
        !           206:                        buf[i] = p[*pos];
        !           207:                else
        !           208:                        break;
        !           209:
        !           210:        assert(i < 1023);
        !           211:        buf[i] = 0;
        !           212:
        !           213:        return(TBL_TOK_WORD);
        !           214: }
        !           215:
        !           216:
        !           217: int
        !           218: tbl_err(struct tbl *tbl)
        !           219: {
        !           220:
        !           221:        (void)fprintf(stderr, "%s\n", strerror(errno));
        !           222:        tbl->part = TBL_PART_ERROR;
        !           223:        return(0);
        !           224: }
        !           225:
        !           226:
        !           227: /* ARGSUSED */
        !           228: int
        !           229: tbl_warnx(struct tbl *tbl, enum tbl_err tok,
        !           230:                const char *f, int line, int pos)
        !           231: {
        !           232:
        !           233:        (void)fprintf(stderr, "%s:%d:%d: %s\n",
        !           234:                        f, line, pos + 1, errnames[tok]);
        !           235:
        !           236:        /* TODO: -Werror */
        !           237:        return(1);
        !           238: }
        !           239:
        !           240:
        !           241: int
        !           242: tbl_errx(struct tbl *tbl, enum tbl_err tok,
        !           243:                const char *f, int line, int pos)
        !           244: {
        !           245:
        !           246:        (void)fprintf(stderr, "%s:%d:%d: %s\n",
        !           247:                        f, line, pos + 1, errnames[tok]);
        !           248:
        !           249:        tbl->part = TBL_PART_ERROR;
        !           250:        return(0);
        !           251: }
        !           252:
        !           253:
        !           254: struct tbl *
        !           255: tbl_alloc(void)
        !           256: {
        !           257:        struct tbl      *p;
        !           258:
        !           259:        if (NULL == (p = malloc(sizeof(struct tbl))))
        !           260:                return(NULL);
        !           261:
        !           262:        tbl_init(p);
        !           263:        return(p);
        !           264: }
        !           265:
        !           266:
        !           267: void
        !           268: tbl_free(struct tbl *p)
        !           269: {
        !           270:
        !           271:        tbl_clear(p);
        !           272:        free(p);
        !           273: }
        !           274:
        !           275:
        !           276: void
        !           277: tbl_reset(struct tbl *tbl)
        !           278: {
        !           279:
        !           280:        tbl_clear(tbl);
        !           281:        tbl_init(tbl);
        !           282: }
        !           283:
        !           284:
        !           285: struct tbl_span *
        !           286: tbl_span_alloc(struct tbl *tbl)
        !           287: {
        !           288:        struct tbl_span *p, *pp;
        !           289:        struct tbl_row  *row;
        !           290:
        !           291:        if (NULL == (p = calloc(1, sizeof(struct tbl_span)))) {
        !           292:                (void)tbl_err(tbl);
        !           293:                return(NULL);
        !           294:        }
        !           295:
        !           296:        TAILQ_INIT(&p->data);
        !           297:        TAILQ_INSERT_TAIL(&tbl->span, p, entries);
        !           298:
        !           299:        /* LINTED */
        !           300:        pp = TAILQ_PREV(p, tbl_spanh, entries);
        !           301:
        !           302:        if (pp) {
        !           303:                row = TAILQ_NEXT(pp->row, entries);
        !           304:                if (NULL == row)
        !           305:                        row = pp->row;
        !           306:        } else {
        !           307:                row = TAILQ_FIRST(&tbl->row);
        !           308:        }
        !           309:
        !           310:        assert(row);
        !           311:        p->row = row;
        !           312:        p->tbl = tbl;
        !           313:        return(p);
        !           314: }
        !           315:
        !           316:
        !           317: struct tbl_row *
        !           318: tbl_row_alloc(struct tbl *tbl)
        !           319: {
        !           320:        struct tbl_row  *p;
        !           321:
        !           322:        if (NULL == (p = calloc(1, sizeof(struct tbl_row)))) {
        !           323:                (void)tbl_err(tbl);
        !           324:                return(NULL);
        !           325:        }
        !           326:
        !           327:        TAILQ_INIT(&p->cell);
        !           328:        TAILQ_INSERT_TAIL(&tbl->row, p, entries);
        !           329:        p->tbl = tbl;
        !           330:        return(p);
        !           331: }
        !           332:
        !           333:
        !           334: static void
        !           335: headadj(const struct tbl_cell *cell, struct tbl_head *head)
        !           336: {
        !           337:        if (TBL_CELL_VERT != cell->pos &&
        !           338:                        TBL_CELL_DVERT != cell->pos) {
        !           339:                head->pos = TBL_HEAD_DATA;
        !           340:                return;
        !           341:        }
        !           342:        if (TBL_CELL_VERT == cell->pos)
        !           343:                if (TBL_HEAD_DVERT != head->pos)
        !           344:                        head->pos = TBL_HEAD_VERT;
        !           345:        if (TBL_CELL_DVERT == cell->pos)
        !           346:                head->pos = TBL_HEAD_DVERT;
        !           347: }
        !           348:
        !           349:
        !           350: static struct tbl_head *
        !           351: tbl_head_alloc(struct tbl *tbl)
        !           352: {
        !           353:        struct tbl_head *p;
        !           354:
        !           355:        if (NULL == (p = calloc(1, sizeof(struct tbl_head)))) {
        !           356:                (void)tbl_err(tbl);
        !           357:                return(NULL);
        !           358:        }
        !           359:        p->tbl = tbl;
        !           360:        return(p);
        !           361: }
        !           362:
        !           363:
        !           364: struct tbl_cell *
        !           365: tbl_cell_alloc(struct tbl_row *rp, enum tbl_cellt pos)
        !           366: {
        !           367:        struct tbl_cell *p, *pp;
        !           368:        struct tbl_head *h, *hp;
        !           369:
        !           370:        if (NULL == (p = calloc(1, sizeof(struct tbl_cell)))) {
        !           371:                (void)tbl_err(rp->tbl);
        !           372:                return(NULL);
        !           373:        }
        !           374:
        !           375:        TAILQ_INSERT_TAIL(&rp->cell, p, entries);
        !           376:        p->pos = pos;
        !           377:        p->row = rp;
        !           378:
        !           379:        /*
        !           380:         * This is a little bit complicated.  Here we determine the
        !           381:         * header the corresponds to a cell.  We add headers dynamically
        !           382:         * when need be or re-use them, otherwise.  As an example, given
        !           383:         * the following:
        !           384:         *
        !           385:         *      1  c || l
        !           386:         *      2  | c | l
        !           387:         *      3  l l
        !           388:         *      3  || c | l |.
        !           389:         *
        !           390:         * We first add the new headers (as there are none) in (1); then
        !           391:         * in (2) we insert the first spanner (as it doesn't match up
        !           392:         * with the header); then we re-use the prior data headers,
        !           393:         * skipping over the spanners; then we re-use everything and add
        !           394:         * a last spanner.  Note that VERT headers are made into DVERT
        !           395:         * ones.
        !           396:         */
        !           397:
        !           398:        /* LINTED */
        !           399:        pp = TAILQ_PREV(p, tbl_cellh, entries);
        !           400:
        !           401:        h = pp ? TAILQ_NEXT(pp->head, entries) :
        !           402:                TAILQ_FIRST(&rp->tbl->head);
        !           403:
        !           404:        if (h) {
        !           405:                /* Re-use data header. */
        !           406:                if (TBL_HEAD_DATA == h->pos &&
        !           407:                                (TBL_CELL_VERT != p->pos &&
        !           408:                                 TBL_CELL_DVERT != p->pos)) {
        !           409:                        p->head = h;
        !           410:                        return(p);
        !           411:                }
        !           412:
        !           413:                /* Re-use spanner header. */
        !           414:                if (TBL_HEAD_DATA != h->pos &&
        !           415:                                (TBL_CELL_VERT == p->pos ||
        !           416:                                 TBL_CELL_DVERT == p->pos)) {
        !           417:                        headadj(p, h);
        !           418:                        p->head = h;
        !           419:                        return(p);
        !           420:                }
        !           421:
        !           422:                /* Right-shift headers with a new spanner. */
        !           423:                if (TBL_HEAD_DATA == h->pos &&
        !           424:                                (TBL_CELL_VERT == p->pos ||
        !           425:                                 TBL_CELL_DVERT == p->pos)) {
        !           426:                        if (NULL == (hp = tbl_head_alloc(rp->tbl)))
        !           427:                                return(NULL);
        !           428:                        TAILQ_INSERT_BEFORE(h, hp, entries);
        !           429:                        headadj(p, hp);
        !           430:                        p->head = hp;
        !           431:                        return(p);
        !           432:                }
        !           433:
        !           434:                h = TAILQ_NEXT(h, entries);
        !           435:                if (h) {
        !           436:                        headadj(p, h);
        !           437:                        p->head = h;
        !           438:                        return(p);
        !           439:                }
        !           440:
        !           441:                /* Fall through to default case... */
        !           442:        }
        !           443:
        !           444:        if (NULL == (hp = tbl_head_alloc(rp->tbl)))
        !           445:                return(NULL);
        !           446:        TAILQ_INSERT_TAIL(&rp->tbl->head, hp, entries);
        !           447:        headadj(p, hp);
        !           448:        p->head = hp;
        !           449:        return(p);
        !           450: }
        !           451:
        !           452:
        !           453: struct tbl_data *
        !           454: tbl_data_alloc(struct tbl_span *sp)
        !           455: {
        !           456:        struct tbl_data *p;
        !           457:        struct tbl_cell *cp;
        !           458:        struct tbl_data *dp;
        !           459:
        !           460:        if (NULL == (p = calloc(1, sizeof(struct tbl_data)))) {
        !           461:                (void)tbl_err(sp->row->tbl);
        !           462:                return(NULL);
        !           463:        }
        !           464:
        !           465:        cp = NULL;
        !           466:        /* LINTED */
        !           467:        if (NULL == (dp = TAILQ_LAST(&sp->data, tbl_datah)))
        !           468:                cp = TAILQ_FIRST(&sp->row->cell);
        !           469:        else if (dp->cell)
        !           470:                cp = TAILQ_NEXT(dp->cell, entries);
        !           471:
        !           472:        TAILQ_INSERT_TAIL(&sp->data, p, entries);
        !           473:
        !           474:        if (cp && (TBL_CELL_VERT == cp->pos ||
        !           475:                                TBL_CELL_DVERT == cp->pos))
        !           476:                cp = TAILQ_NEXT(cp, entries);
        !           477:
        !           478:        p->span = sp;
        !           479:        p->cell = cp;
        !           480:        return(p);
        !           481: }
        !           482:
        !           483:
        !           484: static void
        !           485: tbl_clear(struct tbl *p)
        !           486: {
        !           487:        struct tbl_span *span;
        !           488:        struct tbl_head *head;
        !           489:        struct tbl_row  *row;
        !           490:
        !           491:        /* LINTED */
        !           492:        while ((span = TAILQ_FIRST(&p->span))) {
        !           493:                TAILQ_REMOVE(&p->span, span, entries);
        !           494:                tbl_span_free(span);
        !           495:        }
        !           496:        /* LINTED */
        !           497:        while ((row = TAILQ_FIRST(&p->row))) {
        !           498:                TAILQ_REMOVE(&p->row, row, entries);
        !           499:                tbl_row_free(row);
        !           500:        }
        !           501:        /* LINTED */
        !           502:        while ((head = TAILQ_FIRST(&p->head))) {
        !           503:                TAILQ_REMOVE(&p->head, head, entries);
        !           504:                free(head);
        !           505:        }
        !           506: }
        !           507:
        !           508:
        !           509: static void
        !           510: tbl_span_free(struct tbl_span *p)
        !           511: {
        !           512:        struct tbl_data *data;
        !           513:
        !           514:        /* LINTED */
        !           515:        while ((data = TAILQ_FIRST(&p->data))) {
        !           516:                TAILQ_REMOVE(&p->data, data, entries);
        !           517:                tbl_data_free(data);
        !           518:        }
        !           519:        free(p);
        !           520: }
        !           521:
        !           522:
        !           523: static void
        !           524: tbl_data_free(struct tbl_data *p)
        !           525: {
        !           526:
        !           527:        if (p->string)
        !           528:                free(p->string);
        !           529:        free(p);
        !           530: }
        !           531:
        !           532:
        !           533: static void
        !           534: tbl_row_free(struct tbl_row *p)
        !           535: {
        !           536:        struct tbl_cell *cell;
        !           537:
        !           538:        /* LINTED */
        !           539:        while ((cell = TAILQ_FIRST(&p->cell))) {
        !           540:                TAILQ_REMOVE(&p->cell, cell, entries);
        !           541:                free(cell);
        !           542:        }
        !           543:        free(p);
        !           544: }