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

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