[BACK]Return to tbl_term.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/tbl_term.c, Revision 1.21

1.21    ! schwarze    1: /*     $Id: tbl_term.c,v 1.20 2011/07/17 15:43:00 kristaps Exp $ */
1.1       kristaps    2: /*
1.20      kristaps    3:  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.19      schwarze    4:  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1       kristaps    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18: #ifdef HAVE_CONFIG_H
                     19: #include "config.h"
                     20: #endif
                     21:
                     22: #include <assert.h>
                     23: #include <stdio.h>
                     24: #include <stdlib.h>
                     25: #include <string.h>
                     26:
                     27: #include "mandoc.h"
                     28: #include "out.h"
                     29: #include "term.h"
                     30:
1.11      kristaps   31: static size_t  term_tbl_len(size_t, void *);
                     32: static size_t  term_tbl_strlen(const char *, void *);
1.12      kristaps   33: static void    tbl_char(struct termp *, char, size_t);
1.11      kristaps   34: static void    tbl_data(struct termp *, const struct tbl *,
                     35:                        const struct tbl_dat *,
                     36:                        const struct roffcol *);
1.21    ! schwarze   37: static size_t  tbl_rulewidth(struct termp *, const struct tbl_head *);
        !            38: static void    tbl_hframe(struct termp *, const struct tbl_span *, int);
1.11      kristaps   39: static void    tbl_literal(struct termp *, const struct tbl_dat *,
                     40:                        const struct roffcol *);
                     41: static void    tbl_number(struct termp *, const struct tbl *,
                     42:                        const struct tbl_dat *,
                     43:                        const struct roffcol *);
                     44: static void    tbl_hrule(struct termp *, const struct tbl_span *);
                     45: static void    tbl_vrule(struct termp *, const struct tbl_head *);
                     46:
                     47:
                     48: static size_t
                     49: term_tbl_strlen(const char *p, void *arg)
                     50: {
                     51:
                     52:        return(term_strlen((const struct termp *)arg, p));
                     53: }
                     54:
                     55: static size_t
                     56: term_tbl_len(size_t sz, void *arg)
                     57: {
                     58:
                     59:        return(term_len((const struct termp *)arg, sz));
                     60: }
1.1       kristaps   61:
                     62: void
                     63: term_tbl(struct termp *tp, const struct tbl_span *sp)
                     64: {
1.11      kristaps   65:        const struct tbl_head   *hp;
                     66:        const struct tbl_dat    *dp;
                     67:        struct roffcol          *col;
1.16      kristaps   68:        int                      spans;
1.12      kristaps   69:        size_t                   rmargin, maxrmargin;
1.11      kristaps   70:
                     71:        rmargin = tp->rmargin;
                     72:        maxrmargin = tp->maxrmargin;
                     73:
                     74:        tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
1.1       kristaps   75:
1.4       kristaps   76:        /* Inhibit printing of spaces: we do padding ourselves. */
                     77:
                     78:        tp->flags |= TERMP_NONOSPACE;
                     79:        tp->flags |= TERMP_NOSPACE;
                     80:
                     81:        /*
1.11      kristaps   82:         * The first time we're invoked for a given table block,
                     83:         * calculate the table widths and decimal positions.
1.4       kristaps   84:         */
                     85:
1.2       kristaps   86:        if (TBL_SPAN_FIRST & sp->flags) {
1.11      kristaps   87:                term_flushln(tp);
                     88:
                     89:                tp->tbl.len = term_tbl_len;
                     90:                tp->tbl.slen = term_tbl_strlen;
                     91:                tp->tbl.arg = tp;
1.4       kristaps   92:
1.11      kristaps   93:                tblcalc(&tp->tbl, sp);
1.2       kristaps   94:        }
1.1       kristaps   95:
1.4       kristaps   96:        /* Horizontal frame at the start of boxed tables. */
                     97:
1.21    ! schwarze   98:        if (TBL_SPAN_FIRST & sp->flags) {
        !            99:                if (TBL_OPT_DBOX & sp->tbl->opts)
        !           100:                        tbl_hframe(tp, sp, 1);
        !           101:                if (TBL_OPT_DBOX & sp->tbl->opts ||
        !           102:                    TBL_OPT_BOX  & sp->tbl->opts)
        !           103:                        tbl_hframe(tp, sp, 0);
        !           104:        }
1.1       kristaps  105:
1.4       kristaps  106:        /* Vertical frame at the start of each row. */
1.1       kristaps  107:
1.21    ! schwarze  108:        if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
        !           109:                term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
        !           110:                        TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
1.1       kristaps  111:
1.4       kristaps  112:        /*
                    113:         * Now print the actual data itself depending on the span type.
                    114:         * Spanner spans get a horizontal rule; data spanners have their
                    115:         * data printed by matching data to header.
                    116:         */
                    117:
1.1       kristaps  118:        switch (sp->pos) {
                    119:        case (TBL_SPAN_HORIZ):
                    120:                /* FALLTHROUGH */
                    121:        case (TBL_SPAN_DHORIZ):
                    122:                tbl_hrule(tp, sp);
                    123:                break;
1.4       kristaps  124:        case (TBL_SPAN_DATA):
                    125:                /* Iterate over template headers. */
                    126:                dp = sp->first;
1.16      kristaps  127:                spans = 0;
1.4       kristaps  128:                for (hp = sp->head; hp; hp = hp->next) {
1.16      kristaps  129:                        /*
                    130:                         * If the current data header is invoked during
                    131:                         * a spanner ("spans" > 0), don't emit anything
                    132:                         * at all.
                    133:                         */
1.4       kristaps  134:                        switch (hp->pos) {
                    135:                        case (TBL_HEAD_VERT):
                    136:                                /* FALLTHROUGH */
                    137:                        case (TBL_HEAD_DVERT):
1.16      kristaps  138:                                if (spans <= 0)
                    139:                                        tbl_vrule(tp, hp);
1.4       kristaps  140:                                continue;
                    141:                        case (TBL_HEAD_DATA):
                    142:                                break;
                    143:                        }
1.11      kristaps  144:
1.16      kristaps  145:                        if (--spans >= 0)
                    146:                                continue;
                    147:
1.21    ! schwarze  148:                        /*
        !           149:                         * All cells get a leading blank, except the
        !           150:                         * first one and those after double rulers.
        !           151:                         */
        !           152:
        !           153:                        if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
        !           154:                                tbl_char(tp, ASCII_NBRSP, 1);
        !           155:
1.11      kristaps  156:                        col = &tp->tbl.cols[hp->ident];
                    157:                        tbl_data(tp, sp->tbl, dp, col);
1.1       kristaps  158:
1.21    ! schwarze  159:                        /* No trailing blanks. */
        !           160:
        !           161:                        if (NULL == hp->next)
        !           162:                                break;
        !           163:
        !           164:                        /*
        !           165:                         * Add another blank between cells,
        !           166:                         * or two when there is no vertical ruler.
        !           167:                         */
        !           168:
        !           169:                        tbl_char(tp, ASCII_NBRSP,
        !           170:                            TBL_HEAD_VERT  == hp->next->pos ||
        !           171:                            TBL_HEAD_DVERT == hp->next->pos ? 1 : 2);
        !           172:
1.16      kristaps  173:                        /*
                    174:                         * Go to the next data cell and assign the
                    175:                         * number of subsequent spans, if applicable.
                    176:                         */
                    177:
                    178:                        if (dp) {
                    179:                                spans = dp->spans;
1.1       kristaps  180:                                dp = dp->next;
1.16      kristaps  181:                        }
1.1       kristaps  182:                }
1.4       kristaps  183:                break;
1.1       kristaps  184:        }
                    185:
1.21    ! schwarze  186:        /* Vertical frame at the end of each row. */
        !           187:
        !           188:        if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
        !           189:                term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
        !           190:                        TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
1.1       kristaps  191:        term_flushln(tp);
                    192:
1.4       kristaps  193:        /*
                    194:         * If we're the last row, clean up after ourselves: clear the
                    195:         * existing table configuration and set it to NULL.
                    196:         */
                    197:
1.2       kristaps  198:        if (TBL_SPAN_LAST & sp->flags) {
1.21    ! schwarze  199:                if (TBL_OPT_DBOX & sp->tbl->opts ||
        !           200:                    TBL_OPT_BOX  & sp->tbl->opts)
        !           201:                        tbl_hframe(tp, sp, 0);
        !           202:                if (TBL_OPT_DBOX & sp->tbl->opts)
        !           203:                        tbl_hframe(tp, sp, 1);
1.11      kristaps  204:                assert(tp->tbl.cols);
                    205:                free(tp->tbl.cols);
                    206:                tp->tbl.cols = NULL;
1.2       kristaps  207:        }
1.1       kristaps  208:
                    209:        tp->flags &= ~TERMP_NONOSPACE;
1.11      kristaps  210:        tp->rmargin = rmargin;
                    211:        tp->maxrmargin = maxrmargin;
1.1       kristaps  212:
                    213: }
                    214:
1.21    ! schwarze  215: /*
        !           216:  * Horizontal rules extend across the entire table.
        !           217:  * Calculate the width by iterating over columns.
        !           218:  */
        !           219: static size_t
        !           220: tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
        !           221: {
        !           222:        size_t           width;
        !           223:
        !           224:        width = tp->tbl.cols[hp->ident].width;
        !           225:        if (TBL_HEAD_DATA == hp->pos) {
        !           226:                /* Account for leading blanks. */
        !           227:                if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
        !           228:                        width++;
        !           229:                /* Account for trailing blanks. */
        !           230:                width++;
        !           231:                if (hp->next &&
        !           232:                    TBL_HEAD_VERT  != hp->next->pos &&
        !           233:                    TBL_HEAD_DVERT != hp->next->pos)
        !           234:                        width++;
        !           235:        }
        !           236:        return(width);
        !           237: }
        !           238:
        !           239: /*
        !           240:  * Rules inside the table can be single or double
        !           241:  * and have crossings with vertical rules marked with pluses.
        !           242:  */
1.1       kristaps  243: static void
                    244: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
                    245: {
                    246:        const struct tbl_head *hp;
                    247:        char             c;
                    248:
                    249:        c = '-';
                    250:        if (TBL_SPAN_DHORIZ == sp->pos)
                    251:                c = '=';
                    252:
1.21    ! schwarze  253:        for (hp = sp->head; hp; hp = hp->next)
        !           254:                tbl_char(tp,
        !           255:                    TBL_HEAD_DATA == hp->pos ? c : '+',
        !           256:                    tbl_rulewidth(tp, hp));
1.1       kristaps  257: }
                    258:
1.21    ! schwarze  259: /*
        !           260:  * Rules above and below the table are always single
        !           261:  * and have an additional plus at the beginning and end.
        !           262:  * For double frames, this function is called twice,
        !           263:  * and the outer one does not have crossings.
        !           264:  */
1.1       kristaps  265: static void
1.21    ! schwarze  266: tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
1.1       kristaps  267: {
                    268:        const struct tbl_head *hp;
                    269:
                    270:        term_word(tp, "+");
1.21    ! schwarze  271:        for (hp = sp->head; hp; hp = hp->next)
        !           272:                tbl_char(tp,
        !           273:                    outer || TBL_HEAD_DATA == hp->pos ? '-' : '+',
        !           274:                    tbl_rulewidth(tp, hp));
1.1       kristaps  275:        term_word(tp, "+");
                    276:        term_flushln(tp);
                    277: }
                    278:
                    279: static void
                    280: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2       kristaps  281:                const struct tbl_dat *dp,
1.11      kristaps  282:                const struct roffcol *col)
1.1       kristaps  283: {
                    284:
                    285:        if (NULL == dp) {
1.11      kristaps  286:                tbl_char(tp, ASCII_NBRSP, col->width);
1.1       kristaps  287:                return;
                    288:        }
1.16      kristaps  289:        assert(dp->layout);
1.1       kristaps  290:
                    291:        switch (dp->pos) {
1.10      kristaps  292:        case (TBL_DATA_NONE):
1.11      kristaps  293:                tbl_char(tp, ASCII_NBRSP, col->width);
1.10      kristaps  294:                return;
1.1       kristaps  295:        case (TBL_DATA_HORIZ):
                    296:                /* FALLTHROUGH */
1.7       kristaps  297:        case (TBL_DATA_NHORIZ):
1.11      kristaps  298:                tbl_char(tp, '-', col->width);
1.7       kristaps  299:                return;
                    300:        case (TBL_DATA_NDHORIZ):
                    301:                /* FALLTHROUGH */
1.1       kristaps  302:        case (TBL_DATA_DHORIZ):
1.11      kristaps  303:                tbl_char(tp, '=', col->width);
1.1       kristaps  304:                return;
                    305:        default:
                    306:                break;
                    307:        }
                    308:
1.16      kristaps  309:        switch (dp->layout->pos) {
1.1       kristaps  310:        case (TBL_CELL_HORIZ):
1.11      kristaps  311:                tbl_char(tp, '-', col->width);
1.7       kristaps  312:                break;
1.1       kristaps  313:        case (TBL_CELL_DHORIZ):
1.11      kristaps  314:                tbl_char(tp, '=', col->width);
1.1       kristaps  315:                break;
                    316:        case (TBL_CELL_LONG):
                    317:                /* FALLTHROUGH */
                    318:        case (TBL_CELL_CENTRE):
                    319:                /* FALLTHROUGH */
                    320:        case (TBL_CELL_LEFT):
                    321:                /* FALLTHROUGH */
                    322:        case (TBL_CELL_RIGHT):
1.11      kristaps  323:                tbl_literal(tp, dp, col);
1.1       kristaps  324:                break;
                    325:        case (TBL_CELL_NUMBER):
1.11      kristaps  326:                tbl_number(tp, tbl, dp, col);
1.18      kristaps  327:                break;
                    328:        case (TBL_CELL_DOWN):
                    329:                tbl_char(tp, ASCII_NBRSP, col->width);
1.1       kristaps  330:                break;
                    331:        default:
                    332:                abort();
                    333:                /* NOTREACHED */
                    334:        }
                    335: }
1.11      kristaps  336:
1.1       kristaps  337: static void
1.11      kristaps  338: tbl_vrule(struct termp *tp, const struct tbl_head *hp)
1.1       kristaps  339: {
                    340:
                    341:        switch (hp->pos) {
                    342:        case (TBL_HEAD_VERT):
                    343:                term_word(tp, "|");
                    344:                break;
                    345:        case (TBL_HEAD_DVERT):
                    346:                term_word(tp, "||");
                    347:                break;
                    348:        default:
                    349:                break;
                    350:        }
                    351: }
                    352:
                    353: static void
1.12      kristaps  354: tbl_char(struct termp *tp, char c, size_t len)
1.1       kristaps  355: {
1.12      kristaps  356:        size_t          i, sz;
                    357:        char            cp[2];
                    358:
                    359:        cp[0] = c;
                    360:        cp[1] = '\0';
1.1       kristaps  361:
1.3       kristaps  362:        sz = term_strlen(tp, cp);
                    363:
                    364:        for (i = 0; i < len; i += sz)
1.1       kristaps  365:                term_word(tp, cp);
                    366: }
                    367:
                    368: static void
1.11      kristaps  369: tbl_literal(struct termp *tp, const struct tbl_dat *dp,
                    370:                const struct roffcol *col)
1.1       kristaps  371: {
1.21    ! schwarze  372:        size_t           len, padl, padr;
1.1       kristaps  373:
1.17      kristaps  374:        assert(dp->string);
1.21    ! schwarze  375:        len = term_strlen(tp, dp->string);
        !           376:        padr = col->width > len ? col->width - len : 0;
        !           377:        padl = 0;
1.1       kristaps  378:
1.16      kristaps  379:        switch (dp->layout->pos) {
1.1       kristaps  380:        case (TBL_CELL_LONG):
1.21    ! schwarze  381:                padl = term_len(tp, 1);
        !           382:                padr = padr > padl ? padr - padl : 0;
1.1       kristaps  383:                break;
                    384:        case (TBL_CELL_CENTRE):
1.21    ! schwarze  385:                if (2 > padr)
1.19      schwarze  386:                        break;
1.21    ! schwarze  387:                padl = padr / 2;
1.19      schwarze  388:                padr -= padl;
1.1       kristaps  389:                break;
                    390:        case (TBL_CELL_RIGHT):
1.21    ! schwarze  391:                padl = padr;
        !           392:                padr = 0;
1.1       kristaps  393:                break;
                    394:        default:
                    395:                break;
                    396:        }
                    397:
                    398:        tbl_char(tp, ASCII_NBRSP, padl);
1.17      kristaps  399:        term_word(tp, dp->string);
1.21    ! schwarze  400:        tbl_char(tp, ASCII_NBRSP, padr);
1.1       kristaps  401: }
                    402:
                    403: static void
1.11      kristaps  404: tbl_number(struct termp *tp, const struct tbl *tbl,
1.2       kristaps  405:                const struct tbl_dat *dp,
1.11      kristaps  406:                const struct roffcol *col)
1.1       kristaps  407: {
1.11      kristaps  408:        char            *cp;
                    409:        char             buf[2];
                    410:        size_t           sz, psz, ssz, d, padl;
                    411:        int              i;
1.1       kristaps  412:
                    413:        /*
                    414:         * See calc_data_number().  Left-pad by taking the offset of our
                    415:         * and the maximum decimal; right-pad by the remaining amount.
                    416:         */
                    417:
1.17      kristaps  418:        assert(dp->string);
1.2       kristaps  419:
1.17      kristaps  420:        sz = term_strlen(tp, dp->string);
1.2       kristaps  421:
1.11      kristaps  422:        buf[0] = tbl->decimal;
                    423:        buf[1] = '\0';
1.2       kristaps  424:
1.11      kristaps  425:        psz = term_strlen(tp, buf);
1.10      kristaps  426:
1.17      kristaps  427:        if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
1.5       kristaps  428:                buf[1] = '\0';
1.17      kristaps  429:                for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
                    430:                        buf[0] = dp->string[i];
1.5       kristaps  431:                        ssz += term_strlen(tp, buf);
                    432:                }
                    433:                d = ssz + psz;
                    434:        } else
                    435:                d = sz + psz;
1.2       kristaps  436:
1.11      kristaps  437:        padl = col->decimal - d;
1.2       kristaps  438:
1.11      kristaps  439:        tbl_char(tp, ASCII_NBRSP, padl);
1.17      kristaps  440:        term_word(tp, dp->string);
1.21    ! schwarze  441:        if (col->width > sz + padl)
        !           442:                tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
1.2       kristaps  443: }
                    444:

CVSweb