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

Annotation of mandoc/tbl_term.c, Revision 1.2

1.2     ! kristaps    1: /*     $Id: tbl_term.c,v 1.1 2011/01/02 12:21:07 kristaps Exp $ */
1.1       kristaps    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: #ifdef HAVE_CONFIG_H
                     18: #include "config.h"
                     19: #endif
                     20:
                     21: #include <assert.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
                     26: #include "mandoc.h"
                     27: #include "out.h"
                     28: #include "term.h"
                     29:
                     30: /* FIXME: `n' modifier doesn't always do the right thing. */
                     31: /* FIXME: `n' modifier doesn't use the cell-spacing buffer. */
                     32:
                     33: static inline void      tbl_char(struct termp *, char, int);
                     34: static void             tbl_hframe(struct termp *,
                     35:                                const struct tbl_span *);
                     36: static void             tbl_data_number(struct termp *,
                     37:                                const struct tbl *,
1.2     ! kristaps   38:                                const struct tbl_dat *,
        !            39:                                const struct termp_tbl *);
1.1       kristaps   40: static void             tbl_data_literal(struct termp *,
1.2     ! kristaps   41:                                const struct tbl_dat *,
        !            42:                                const struct termp_tbl *);
1.1       kristaps   43: static void             tbl_data_spanner(struct termp *,
1.2     ! kristaps   44:                                const struct tbl_dat *,
        !            45:                                const struct termp_tbl *);
1.1       kristaps   46: static void             tbl_data(struct termp *, const struct tbl *,
1.2     ! kristaps   47:                                const struct tbl_dat *,
        !            48:                                const struct termp_tbl *);
1.1       kristaps   49: static void             tbl_spanner(struct termp *,
                     50:                                const struct tbl_head *);
                     51: static void             tbl_hrule(struct termp *,
                     52:                                const struct tbl_span *);
1.2     ! kristaps   53: static void             tbl_vframe(struct termp *,
        !            54:                                const struct tbl *);
        !            55: static void             tbl_calc(const struct tbl_span *,
        !            56:                                struct termp_tbl *);
        !            57: static void             tbl_calc_data(const struct tbl *,
        !            58:                                const struct tbl_dat *,
        !            59:                                struct termp_tbl *);
        !            60: static void             tbl_calc_data_literal(const struct tbl_dat *,
        !            61:                                struct termp_tbl *);
        !            62: static void             tbl_calc_data_number(const struct tbl *,
        !            63:                                const struct tbl_dat *,
        !            64:                                struct termp_tbl *);
1.1       kristaps   65:
                     66: void
                     67: term_tbl(struct termp *tp, const struct tbl_span *sp)
                     68: {
                     69:        const struct tbl_head *hp;
                     70:        const struct tbl_dat *dp;
                     71:
1.2     ! kristaps   72:        if (TBL_SPAN_FIRST & sp->flags) {
        !            73:                assert(NULL == tp->tbl);
        !            74:                tp->tbl = calloc
        !            75:                        (sp->tbl->cols, sizeof(struct termp_tbl));
        !            76:                if (NULL == tp->tbl) {
        !            77:                        perror(NULL);
        !            78:                        exit(EXIT_FAILURE);
        !            79:                }
        !            80:                tbl_calc(sp, tp->tbl);
1.1       kristaps   81:                term_flushln(tp);
1.2     ! kristaps   82:        }
1.1       kristaps   83:
                     84:        if (TBL_SPAN_FIRST & sp->flags)
                     85:                tbl_hframe(tp, sp);
                     86:
                     87:        tp->flags |= TERMP_NONOSPACE;
                     88:        tp->flags |= TERMP_NOSPACE;
                     89:
                     90:        tbl_vframe(tp, sp->tbl);
                     91:
                     92:        switch (sp->pos) {
                     93:        case (TBL_SPAN_HORIZ):
                     94:                /* FALLTHROUGH */
                     95:        case (TBL_SPAN_DHORIZ):
                     96:                tbl_hrule(tp, sp);
                     97:                tbl_vframe(tp, sp->tbl);
                     98:                term_newln(tp);
1.2     ! kristaps   99:                goto end;
1.1       kristaps  100:        default:
                    101:                break;
                    102:        }
                    103:
                    104:        dp = sp->first;
                    105:        for (hp = sp->head; hp; hp = hp->next) {
                    106:                switch (hp->pos) {
                    107:                case (TBL_HEAD_VERT):
                    108:                        /* FALLTHROUGH */
                    109:                case (TBL_HEAD_DVERT):
                    110:                        tbl_spanner(tp, hp);
                    111:                        break;
                    112:                case (TBL_HEAD_DATA):
1.2     ! kristaps  113:                        tbl_data(tp, sp->tbl, dp, &tp->tbl[hp->ident]);
1.1       kristaps  114:                        if (dp)
                    115:                                dp = dp->next;
                    116:                        break;
                    117:                default:
                    118:                        abort();
                    119:                        /* NOTREACHED */
                    120:                }
                    121:        }
                    122:
                    123:        tbl_vframe(tp, sp->tbl);
                    124:        term_flushln(tp);
                    125:
1.2     ! kristaps  126: end:
        !           127:        if (TBL_SPAN_LAST & sp->flags) {
1.1       kristaps  128:                tbl_hframe(tp, sp);
1.2     ! kristaps  129:                assert(tp->tbl);
        !           130:                free(tp->tbl);
        !           131:                tp->tbl = NULL;
        !           132:        }
1.1       kristaps  133:
                    134:        tp->flags &= ~TERMP_NONOSPACE;
                    135:
                    136: }
                    137:
                    138: static void
                    139: tbl_hrule(struct termp *tp, const struct tbl_span *sp)
                    140: {
                    141:        const struct tbl_head *hp;
                    142:        char             c;
1.2     ! kristaps  143:        int              width;
1.1       kristaps  144:
                    145:        /*
                    146:         * An hrule extends across the entire table and is demarked by a
                    147:         * standalone `_' or whatnot in lieu of a table row.  Spanning
                    148:         * headers are marked by a `+', as are table boundaries.
                    149:         */
                    150:
                    151:        c = '-';
                    152:        if (TBL_SPAN_DHORIZ == sp->pos)
                    153:                c = '=';
                    154:
                    155:        /* FIXME: don't use `+' between data and a spanner! */
                    156:
                    157:        for (hp = sp->head; hp; hp = hp->next) {
1.2     ! kristaps  158:                width = tp->tbl[hp->ident].width;
1.1       kristaps  159:                switch (hp->pos) {
                    160:                case (TBL_HEAD_DATA):
1.2     ! kristaps  161:                        tbl_char(tp, c, width);
1.1       kristaps  162:                        break;
                    163:                case (TBL_HEAD_DVERT):
1.2     ! kristaps  164:                        tbl_char(tp, '+', width);
1.1       kristaps  165:                        /* FALLTHROUGH */
                    166:                case (TBL_HEAD_VERT):
1.2     ! kristaps  167:                        tbl_char(tp, '+', width);
1.1       kristaps  168:                        break;
                    169:                default:
                    170:                        abort();
                    171:                        /* NOTREACHED */
                    172:                }
                    173:        }
                    174: }
                    175:
                    176: static void
                    177: tbl_hframe(struct termp *tp, const struct tbl_span *sp)
                    178: {
                    179:        const struct tbl_head *hp;
1.2     ! kristaps  180:        int              width;
1.1       kristaps  181:
                    182:        if ( ! (TBL_OPT_BOX & sp->tbl->opts ||
                    183:                        TBL_OPT_DBOX & sp->tbl->opts))
                    184:                return;
                    185:
                    186:        tp->flags |= TERMP_NONOSPACE;
                    187:        tp->flags |= TERMP_NOSPACE;
                    188:
                    189:        /*
                    190:         * Print out the horizontal part of a frame or double frame.  A
                    191:         * double frame has an unbroken `-' outer line the width of the
                    192:         * table, bordered by `+'.  The frame (or inner frame, in the
                    193:         * case of the double frame) is a `-' bordered by `+' and broken
                    194:         * by `+' whenever a span is encountered.
                    195:         */
                    196:
                    197:        if (TBL_OPT_DBOX & sp->tbl->opts) {
                    198:                term_word(tp, "+");
1.2     ! kristaps  199:                for (hp = sp->head; hp; hp = hp->next) {
        !           200:                        width = tp->tbl[hp->ident].width;
        !           201:                        tbl_char(tp, '-', width);
        !           202:                }
1.1       kristaps  203:                term_word(tp, "+");
                    204:                term_flushln(tp);
                    205:        }
                    206:
                    207:        term_word(tp, "+");
                    208:        for (hp = sp->head; hp; hp = hp->next) {
1.2     ! kristaps  209:                width = tp->tbl[hp->ident].width;
1.1       kristaps  210:                switch (hp->pos) {
                    211:                case (TBL_HEAD_DATA):
1.2     ! kristaps  212:                        tbl_char(tp, '-', width);
1.1       kristaps  213:                        break;
                    214:                default:
1.2     ! kristaps  215:                        tbl_char(tp, '+', width);
1.1       kristaps  216:                        break;
                    217:                }
                    218:        }
                    219:        term_word(tp, "+");
                    220:        term_flushln(tp);
                    221: }
                    222:
                    223: static void
                    224: tbl_data(struct termp *tp, const struct tbl *tbl,
1.2     ! kristaps  225:                const struct tbl_dat *dp,
        !           226:                const struct termp_tbl *tbp)
1.1       kristaps  227: {
                    228:        enum tbl_cellt   pos;
                    229:
                    230:        if (NULL == dp) {
1.2     ! kristaps  231:                tbl_char(tp, ASCII_NBRSP, tbp->width);
1.1       kristaps  232:                return;
                    233:        }
                    234:
                    235:        switch (dp->pos) {
                    236:        case (TBL_DATA_HORIZ):
                    237:                /* FALLTHROUGH */
                    238:        case (TBL_DATA_DHORIZ):
1.2     ! kristaps  239:                tbl_data_spanner(tp, dp, tbp);
1.1       kristaps  240:                return;
                    241:        default:
                    242:                break;
                    243:        }
                    244:
                    245:        pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
                    246:
                    247:        switch (pos) {
                    248:        case (TBL_CELL_HORIZ):
                    249:                /* FALLTHROUGH */
                    250:        case (TBL_CELL_DHORIZ):
1.2     ! kristaps  251:                tbl_data_spanner(tp, dp, tbp);
1.1       kristaps  252:                break;
                    253:        case (TBL_CELL_LONG):
                    254:                /* FALLTHROUGH */
                    255:        case (TBL_CELL_CENTRE):
                    256:                /* FALLTHROUGH */
                    257:        case (TBL_CELL_LEFT):
                    258:                /* FALLTHROUGH */
                    259:        case (TBL_CELL_RIGHT):
1.2     ! kristaps  260:                tbl_data_literal(tp, dp, tbp);
1.1       kristaps  261:                break;
                    262:        case (TBL_CELL_NUMBER):
1.2     ! kristaps  263:                tbl_data_number(tp, tbl, dp, tbp);
1.1       kristaps  264:                break;
                    265:        default:
                    266:                abort();
                    267:                /* NOTREACHED */
                    268:        }
                    269: }
                    270: static void
                    271: tbl_spanner(struct termp *tp, const struct tbl_head *hp)
                    272: {
                    273:
                    274:        switch (hp->pos) {
                    275:        case (TBL_HEAD_VERT):
                    276:                term_word(tp, "|");
                    277:                break;
                    278:        case (TBL_HEAD_DVERT):
                    279:                term_word(tp, "||");
                    280:                break;
                    281:        default:
                    282:                break;
                    283:        }
                    284: }
                    285:
                    286: static void
                    287: tbl_vframe(struct termp *tp, const struct tbl *tbl)
                    288: {
                    289:        /* Always just a single vertical line. */
                    290:
                    291:        if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts)
                    292:                term_word(tp, "|");
                    293: }
                    294:
                    295:
                    296: static inline void
                    297: tbl_char(struct termp *tp, char c, int len)
                    298: {
                    299:        int             i;
                    300:        char            cp[2];
                    301:
                    302:        cp[0] = c;
                    303:        cp[1] = '\0';
                    304:
                    305:        for (i = 0; i < len; i++)
                    306:                term_word(tp, cp);
                    307: }
                    308:
                    309: static void
1.2     ! kristaps  310: tbl_data_spanner(struct termp *tp,
        !           311:                const struct tbl_dat *dp,
        !           312:                const struct termp_tbl *tblp)
1.1       kristaps  313: {
                    314:
                    315:        switch (dp->pos) {
                    316:        case (TBL_DATA_HORIZ):
                    317:        case (TBL_DATA_NHORIZ):
1.2     ! kristaps  318:                tbl_char(tp, '-', tblp->width);
1.1       kristaps  319:                break;
                    320:        case (TBL_DATA_DHORIZ):
                    321:        case (TBL_DATA_NDHORIZ):
1.2     ! kristaps  322:                tbl_char(tp, '=', tblp->width);
1.1       kristaps  323:                break;
                    324:        default:
                    325:                break;
                    326:        }
                    327: }
                    328:
                    329: static void
1.2     ! kristaps  330: tbl_data_literal(struct termp *tp,
        !           331:                const struct tbl_dat *dp,
        !           332:                const struct termp_tbl *tblp)
1.1       kristaps  333: {
                    334:        int              padl, padr;
                    335:        enum tbl_cellt   pos;
                    336:
                    337:        padl = padr = 0;
                    338:
                    339:        pos = dp->layout ? dp->layout->pos : TBL_CELL_LEFT;
                    340:
                    341:        switch (pos) {
                    342:        case (TBL_CELL_LONG):
                    343:                padl = 1;
1.2     ! kristaps  344:                padr = tblp->width - (int)strlen(dp->string) - 1;
1.1       kristaps  345:                break;
                    346:        case (TBL_CELL_CENTRE):
1.2     ! kristaps  347:                padl = tblp->width - (int)strlen(dp->string);
1.1       kristaps  348:                if (padl % 2)
                    349:                        padr++;
                    350:                padl /= 2;
                    351:                padr += padl;
                    352:                break;
                    353:        case (TBL_CELL_RIGHT):
1.2     ! kristaps  354:                padl = tblp->width - (int)strlen(dp->string);
1.1       kristaps  355:                break;
                    356:        default:
1.2     ! kristaps  357:                padr = tblp->width - (int)strlen(dp->string);
1.1       kristaps  358:                break;
                    359:        }
                    360:
                    361:        tbl_char(tp, ASCII_NBRSP, padl);
                    362:        term_word(tp, dp->string);
                    363:        tbl_char(tp, ASCII_NBRSP, padr);
                    364: }
                    365:
                    366: static void
                    367: tbl_data_number(struct termp *tp, const struct tbl *tbl,
1.2     ! kristaps  368:                const struct tbl_dat *dp,
        !           369:                const struct termp_tbl *tblp)
1.1       kristaps  370: {
                    371:        char            *decp, pnt;
                    372:        int              d, padl, sz;
                    373:
                    374:        /*
                    375:         * See calc_data_number().  Left-pad by taking the offset of our
                    376:         * and the maximum decimal; right-pad by the remaining amount.
                    377:         */
                    378:
                    379:        sz = (int)strlen(dp->string);
                    380:        pnt = tbl->decimal;
                    381:
                    382:        if (NULL == (decp = strchr(dp->string, pnt))) {
                    383:                d = sz + 1;
                    384:        } else {
                    385:                d = (int)(decp - dp->string) + 1;
                    386:        }
                    387:
1.2     ! kristaps  388:        assert(d <= tblp->decimal);
        !           389:        assert(sz - d <= tblp->width - tblp->decimal);
1.1       kristaps  390:
1.2     ! kristaps  391:        padl = tblp->decimal - d + 1;
        !           392:        assert(tblp->width - sz - padl);
1.1       kristaps  393:
                    394:        tbl_char(tp, ASCII_NBRSP, padl);
                    395:        term_word(tp, dp->string);
1.2     ! kristaps  396:        tbl_char(tp, ASCII_NBRSP, tblp->width - sz - padl);
        !           397: }
        !           398:
        !           399: static void
        !           400: tbl_calc(const struct tbl_span *sp, struct termp_tbl *tblp)
        !           401: {
        !           402:        const struct tbl_dat *dp;
        !           403:        const struct tbl_head *hp;
        !           404:        struct termp_tbl *p;
        !           405:
        !           406:        /* Calculate width as the max of column cells' widths. */
        !           407:
        !           408:        hp = sp->head;
        !           409:
        !           410:        for ( ; sp; sp = sp->next) {
        !           411:                switch (sp->pos) {
        !           412:                case (TBL_DATA_HORIZ):
        !           413:                        /* FALLTHROUGH */
        !           414:                case (TBL_DATA_DHORIZ):
        !           415:                        continue;
        !           416:                default:
        !           417:                        break;
        !           418:                }
        !           419:                for (dp = sp->first; dp; dp = dp->next) {
        !           420:                        if (NULL == dp->layout)
        !           421:                                continue;
        !           422:                        p = &tblp[dp->layout->head->ident];
        !           423:                        tbl_calc_data(sp->tbl, dp, p);
        !           424:                }
        !           425:        }
        !           426:
        !           427:        /* Calculate width as the simple spanner value. */
        !           428:
        !           429:        for ( ; hp; hp = hp->next)
        !           430:                switch (hp->pos) {
        !           431:                case (TBL_HEAD_VERT):
        !           432:                        tblp[hp->ident].width = 1;
        !           433:                        break;
        !           434:                case (TBL_HEAD_DVERT):
        !           435:                        tblp[hp->ident].width = 2;
        !           436:                        break;
        !           437:                default:
        !           438:                        break;
        !           439:                }
        !           440: }
        !           441:
        !           442: static void
        !           443: tbl_calc_data(const struct tbl *tbl,
        !           444:                const struct tbl_dat *dp,
        !           445:                struct termp_tbl *tblp)
        !           446: {
        !           447:
        !           448:        /* Branch down into data sub-types. */
        !           449:
        !           450:        switch (dp->layout->pos) {
        !           451:        case (TBL_CELL_HORIZ):
        !           452:                /* FALLTHROUGH */
        !           453:        case (TBL_CELL_DHORIZ):
        !           454:                tblp->width = 1;
        !           455:                break;
        !           456:        case (TBL_CELL_LONG):
        !           457:                /* FALLTHROUGH */
        !           458:        case (TBL_CELL_CENTRE):
        !           459:                /* FALLTHROUGH */
        !           460:        case (TBL_CELL_LEFT):
        !           461:                /* FALLTHROUGH */
        !           462:        case (TBL_CELL_RIGHT):
        !           463:                tbl_calc_data_literal(dp, tblp);
        !           464:                break;
        !           465:        case (TBL_CELL_NUMBER):
        !           466:                tbl_calc_data_number(tbl, dp, tblp);
        !           467:                break;
        !           468:        default:
        !           469:                abort();
        !           470:                /* NOTREACHED */
        !           471:        }
        !           472: }
        !           473:
        !           474: static void
        !           475: tbl_calc_data_number(const struct tbl *tbl,
        !           476:                const struct tbl_dat *dp, struct termp_tbl *tblp)
        !           477: {
        !           478:        int              sz, d;
        !           479:        char            *cp, pnt;
        !           480:
        !           481:        /*
        !           482:         * First calculate number width and decimal place (last + 1 for
        !           483:         * no-decimal numbers).  If the stored decimal is subsequent
        !           484:         * ours, make our size longer by that difference
        !           485:         * (right-"shifting"); similarly, if ours is subsequent the
        !           486:         * stored, then extend the stored size by the difference.
        !           487:         * Finally, re-assign the stored values.
        !           488:         */
        !           489:
        !           490:        /* TODO: use spacing modifier. */
        !           491:
        !           492:        assert(dp->string);
        !           493:        sz = (int)strlen(dp->string);
        !           494:        pnt = tbl->decimal;
        !           495:
        !           496:        if (NULL == (cp = strchr(dp->string, pnt)))
        !           497:                d = sz + 1;
        !           498:        else
        !           499:                d = (int)(cp - dp->string) + 1;
        !           500:
        !           501:        sz += 2;
        !           502:
        !           503:        if (tblp->decimal > d) {
        !           504:                sz += tblp->decimal - d;
        !           505:                d = tblp->decimal;
        !           506:        } else
        !           507:                tblp->width += d - tblp->decimal;
        !           508:
        !           509:        if (sz > tblp->width)
        !           510:                tblp->width = sz;
        !           511:        if (d > tblp->decimal)
        !           512:                tblp->decimal = d;
        !           513: }
        !           514:
        !           515: static void
        !           516: tbl_calc_data_literal(const struct tbl_dat *dp, struct termp_tbl *tblp)
        !           517: {
        !           518:        int              sz, bufsz;
        !           519:
        !           520:        /*
        !           521:         * Calculate our width and use the spacing, with a minimum
        !           522:         * spacing dictated by position (centre, e.g,. gets a space on
        !           523:         * either side, while right/left get a single adjacent space).
        !           524:         */
        !           525:
        !           526:        assert(dp->string);
        !           527:        sz = (int)strlen(dp->string);
        !           528:
        !           529:        switch (dp->layout->pos) {
        !           530:        case (TBL_CELL_LONG):
        !           531:                /* FALLTHROUGH */
        !           532:        case (TBL_CELL_CENTRE):
        !           533:                bufsz = 2;
        !           534:                break;
        !           535:        default:
        !           536:                bufsz = 1;
        !           537:                break;
        !           538:        }
        !           539:
        !           540:        if (dp->layout->spacing)
        !           541:                bufsz = bufsz > dp->layout->spacing ?
        !           542:                        bufsz : dp->layout->spacing;
        !           543:
        !           544:        sz += bufsz;
        !           545:        if (tblp->width < sz)
        !           546:                tblp->width = sz;
1.1       kristaps  547: }

CVSweb