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

Annotation of src/usr.bin/mandoc/tbl_term.c, Revision 1.4

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