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

Annotation of mandoc/term.c, Revision 1.4

1.4     ! kristaps    1: /* $Id: term.c,v 1.3 2009/02/21 14:56:58 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008 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
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19: #include <assert.h>
1.2       kristaps   20: #include <ctype.h>
1.1       kristaps   21: #include <curses.h>
                     22: #include <err.h>
                     23: #include <stdlib.h>
                     24: #include <stdio.h>
                     25: #include <string.h>
                     26: #include <term.h>
                     27: #include <unistd.h>
                     28:
1.2       kristaps   29: #include "private.h"
1.1       kristaps   30:
1.2       kristaps   31: enum   termesc {
                     32:        ESC_CLEAR,
                     33:        ESC_BOLD,
                     34:        ESC_UNDERLINE
                     35: };
                     36:
                     37: struct termp {
1.3       kristaps   38:        size_t            rmargin;
                     39:        size_t            maxrmargin;
1.2       kristaps   40:        size_t            maxcols;
1.3       kristaps   41:        size_t            offset;
1.2       kristaps   42:        size_t            col;
                     43:        int               flags;
1.3       kristaps   44: #define        TERMP_BOLD       (1 << 0)       /* Embolden words. */
                     45: #define        TERMP_UNDERLINE  (1 << 1)       /* Underline words. */
                     46: #define        TERMP_NOSPACE    (1 << 2)       /* No space before words. */
                     47: #define        TERMP_NOLPAD     (1 << 3)       /* No left-padding. */
                     48: #define        TERMP_NOBREAK    (1 << 4)       /* No break after line */
1.2       kristaps   49:        char             *buf;
                     50: };
                     51:
                     52: struct termact {
                     53:        int             (*pre)(struct termp *,
                     54:                                const struct mdoc_meta *,
1.1       kristaps   55:                                const struct mdoc_node *);
1.2       kristaps   56:        int             (*post)(struct termp *,
                     57:                                const struct mdoc_meta *,
                     58:                                const struct mdoc_node *);
                     59: };
                     60:
                     61: static void              termprint_r(struct termp *,
                     62:                                const struct mdoc_meta *,
                     63:                                const struct mdoc_node *);
                     64: static void              termprint_header(struct termp *,
1.1       kristaps   65:                                const struct mdoc_meta *);
1.2       kristaps   66: static void              termprint_footer(struct termp *,
1.1       kristaps   67:                                const struct mdoc_meta *);
                     68:
1.3       kristaps   69: static int               arg_hasattr(int, size_t,
                     70:                                const struct mdoc_arg *);
                     71: static int               arg_getattr(int, size_t,
                     72:                                const struct mdoc_arg *);
                     73:
1.2       kristaps   74: static void              newln(struct termp *);
                     75: static void              vspace(struct termp *);
                     76: static void              pword(struct termp *, const char *, size_t);
                     77: static void              word(struct termp *, const char *);
                     78:
1.3       kristaps   79: #define        decl_prepost(name, suffix) \
                     80: static int               name##_##suffix(struct termp *, \
                     81:                                const struct mdoc_meta *, \
                     82:                                const struct mdoc_node *)
                     83:
                     84: #define        decl_pre(name)    decl_prepost(name, pre)
                     85: #define        decl_post(name)   decl_prepost(name, post)
                     86:
                     87: decl_pre(termp_fl);
                     88: decl_pre(termp_it);
                     89: decl_pre(termp_nd);
                     90: decl_pre(termp_ns);
                     91: decl_pre(termp_op);
                     92: decl_pre(termp_pp);
                     93: decl_pre(termp_sh);
1.4     ! kristaps   94: decl_pre(termp_xr);
1.3       kristaps   95:
                     96: decl_post(termp_bl);
                     97: decl_post(termp_it);
                     98: decl_post(termp_op);
                     99: decl_post(termp_sh);
                    100:
                    101: decl_pre(termp_bold);
                    102: decl_pre(termp_under);
                    103:
                    104: decl_post(termp_bold);
                    105: decl_post(termp_under);
1.2       kristaps  106:
                    107: const  struct termact termacts[MDOC_MAX] = {
                    108:        { NULL, NULL }, /* \" */
                    109:        { NULL, NULL }, /* Dd */
                    110:        { NULL, NULL }, /* Dt */
                    111:        { NULL, NULL }, /* Os */
                    112:        { termp_sh_pre, termp_sh_post }, /* Sh */
                    113:        { NULL, NULL }, /* Ss */
                    114:        { termp_pp_pre, NULL }, /* Pp */
                    115:        { NULL, NULL }, /* D1 */
                    116:        { NULL, NULL }, /* Dl */
                    117:        { NULL, NULL }, /* Bd */
                    118:        { NULL, NULL }, /* Ed */
                    119:        { NULL, termp_bl_post }, /* Bl */
                    120:        { NULL, NULL }, /* El */
1.3       kristaps  121:        { termp_it_pre, termp_it_post }, /* It */
1.2       kristaps  122:        { NULL, NULL }, /* Ad */
                    123:        { NULL, NULL }, /* An */
                    124:        { termp_under_pre, termp_under_post }, /* Ar */
                    125:        { NULL, NULL }, /* Cd */
                    126:        { NULL, NULL }, /* Cm */
                    127:        { NULL, NULL }, /* Dv */
                    128:        { NULL, NULL }, /* Er */
                    129:        { NULL, NULL }, /* Ev */
                    130:        { NULL, NULL }, /* Ex */
                    131:        { NULL, NULL }, /* Fa */
                    132:        { NULL, NULL }, /* Fd */
                    133:        { termp_fl_pre, termp_bold_post }, /* Fl */
                    134:        { NULL, NULL }, /* Fn */
                    135:        { NULL, NULL }, /* Ft */
                    136:        { NULL, NULL }, /* Ic */
                    137:        { NULL, NULL }, /* In */
                    138:        { NULL, NULL }, /* Li */
                    139:        { termp_nd_pre, NULL }, /* Nd */
                    140:        { termp_bold_pre, termp_bold_post }, /* Nm */
                    141:        { termp_op_pre, termp_op_post }, /* Op */
                    142:        { NULL, NULL }, /* Ot */
                    143:        { NULL, NULL }, /* Pa */
                    144:        { NULL, NULL }, /* Rv */
                    145:        { NULL, NULL }, /* St */
                    146:        { NULL, NULL }, /* Va */
                    147:        { NULL, NULL }, /* Vt */
1.4     ! kristaps  148:        { termp_xr_pre, NULL }, /* Xr */
1.2       kristaps  149:        { NULL, NULL }, /* %A */
                    150:        { NULL, NULL }, /* %B */
                    151:        { NULL, NULL }, /* %D */
                    152:        { NULL, NULL }, /* %I */
                    153:        { NULL, NULL }, /* %J */
                    154:        { NULL, NULL }, /* %N */
                    155:        { NULL, NULL }, /* %O */
                    156:        { NULL, NULL }, /* %P */
                    157:        { NULL, NULL }, /* %R */
                    158:        { NULL, NULL }, /* %T */
                    159:        { NULL, NULL }, /* %V */
                    160:        { NULL, NULL }, /* Ac */
                    161:        { NULL, NULL }, /* Ao */
                    162:        { NULL, NULL }, /* Aq */
                    163:        { NULL, NULL }, /* At */
                    164:        { NULL, NULL }, /* Bc */
                    165:        { NULL, NULL }, /* Bf */
                    166:        { NULL, NULL }, /* Bo */
                    167:        { NULL, NULL }, /* Bq */
                    168:        { NULL, NULL }, /* Bsx */
                    169:        { NULL, NULL }, /* Bx */
                    170:        { NULL, NULL }, /* Db */
                    171:        { NULL, NULL }, /* Dc */
                    172:        { NULL, NULL }, /* Do */
                    173:        { NULL, NULL }, /* Dq */
                    174:        { NULL, NULL }, /* Ec */
                    175:        { NULL, NULL }, /* Ef */
                    176:        { NULL, NULL }, /* Em */
                    177:        { NULL, NULL }, /* Eo */
                    178:        { NULL, NULL }, /* Fx */
                    179:        { NULL, NULL }, /* Ms */
                    180:        { NULL, NULL }, /* No */
                    181:        { termp_ns_pre, NULL }, /* Ns */
                    182:        { NULL, NULL }, /* Nx */
                    183:        { NULL, NULL }, /* Ox */
                    184:        { NULL, NULL }, /* Pc */
                    185:        { NULL, NULL }, /* Pf */
                    186:        { NULL, NULL }, /* Po */
                    187:        { NULL, NULL }, /* Pq */
                    188:        { NULL, NULL }, /* Qc */
                    189:        { NULL, NULL }, /* Ql */
                    190:        { NULL, NULL }, /* Qo */
                    191:        { NULL, NULL }, /* Qq */
                    192:        { NULL, NULL }, /* Re */
                    193:        { NULL, NULL }, /* Rs */
                    194:        { NULL, NULL }, /* Sc */
                    195:        { NULL, NULL }, /* So */
                    196:        { NULL, NULL }, /* Sq */
                    197:        { NULL, NULL }, /* Sm */
                    198:        { NULL, NULL }, /* Sx */
                    199:        { NULL, NULL }, /* Sy */
                    200:        { NULL, NULL }, /* Tn */
                    201:        { NULL, NULL }, /* Ux */
                    202:        { NULL, NULL }, /* Xc */
                    203:        { NULL, NULL }, /* Xo */
                    204:        { NULL, NULL }, /* Fo */
                    205:        { NULL, NULL }, /* Fc */
                    206:        { NULL, NULL }, /* Oo */
                    207:        { NULL, NULL }, /* Oc */
                    208:        { NULL, NULL }, /* Bk */
                    209:        { NULL, NULL }, /* Ek */
                    210:        { NULL, NULL }, /* Bt */
                    211:        { NULL, NULL }, /* Hf */
                    212:        { NULL, NULL }, /* Fr */
                    213:        { NULL, NULL }, /* Ud */
                    214: };
                    215:
                    216:
1.3       kristaps  217: static int
                    218: arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
                    219: {
                    220:
                    221:        return(-1 != arg_getattr(arg, argc, argv));
                    222: }
                    223:
                    224:
                    225: static int
                    226: arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
                    227: {
                    228:        int              i;
                    229:
                    230:        for (i = 0; i < (int)argc; i++)
                    231:                if (argv[i].arg == arg)
                    232:                        return(i);
                    233:        return(-1);
                    234: }
                    235:
                    236:
1.2       kristaps  237: static void
1.3       kristaps  238: flushln(struct termp *p)
1.2       kristaps  239: {
                    240:        size_t           i, j, vsz, vis, maxvis;
                    241:
1.3       kristaps  242:        /*
                    243:         * First, establish the maximum columns of "visible" content.
                    244:         * This is usually the difference between the right-margin and
                    245:         * an indentation, but can be, for tagged lists or columns, a
                    246:         * small set of values.
                    247:         */
                    248:
                    249:        assert(p->offset < p->rmargin);
                    250:        maxvis = p->rmargin - p->offset;
1.2       kristaps  251:        vis = 0;
                    252:
1.3       kristaps  253:        /*
                    254:         * If in the standard case (left-justified), then begin with our
                    255:         * indentation, otherwise (columns, etc.) just start spitting
                    256:         * out text.
                    257:         */
                    258:
                    259:        if ( ! (p->flags & TERMP_NOLPAD))
                    260:                /* LINTED */
                    261:                for (j = 0; j < p->offset; j++)
                    262:                        putchar(' ');
1.2       kristaps  263:
                    264:        for (i = 0; i < p->col; i++) {
1.3       kristaps  265:                /*
                    266:                 * Count up visible word characters.  Control sequences
                    267:                 * (starting with the CSI) aren't counted.
                    268:                 */
                    269:                assert( ! isspace(p->buf[i]));
                    270:
                    271:                /* LINTED */
1.2       kristaps  272:                for (j = i, vsz = 0; j < p->col; j++) {
                    273:                        if (isspace(p->buf[j]))
                    274:                                break;
                    275:                        else if (27 == p->buf[j]) {
                    276:                                assert(j + 4 <= p->col);
                    277:                                j += 3;
                    278:                        } else
                    279:                                vsz++;
                    280:                }
                    281:                assert(vsz > 0);
                    282:
1.3       kristaps  283:                /*
                    284:                 * If a word is too long and we're within a line, put it
                    285:                 * on the next line.  Puke if we're being asked to write
                    286:                 * something that will exceed the right margin (i.e.,
                    287:                 * from a fresh line or when we're not allowed to break
                    288:                 * the line with TERMP_NOBREAK).
                    289:                 */
                    290:
1.2       kristaps  291:                if (vis && vis + vsz >= maxvis) {
1.3       kristaps  292:                        /* FIXME */
                    293:                        if (p->flags & TERMP_NOBREAK)
                    294:                                errx(1, "word breaks right margin");
1.2       kristaps  295:                        putchar('\n');
1.3       kristaps  296:                        for (j = 0; j < p->offset; j++)
1.2       kristaps  297:                                putchar(' ');
                    298:                        vis = 0;
1.3       kristaps  299:                } else if (vis + vsz >= maxvis) {
                    300:                        /* FIXME */
                    301:                        errx(1, "word breaks right margin");
                    302:                }
                    303:
                    304:                /*
                    305:                 * Write out the word and a trailing space.  Omit the
                    306:                 * space if we're the last word in the line.
                    307:                 */
1.2       kristaps  308:
                    309:                for ( ; i < p->col; i++) {
                    310:                        if (isspace(p->buf[i]))
                    311:                                break;
                    312:                        putchar(p->buf[i]);
                    313:                }
                    314:                vis += vsz;
                    315:                if (i < p->col) {
                    316:                        putchar(' ');
                    317:                        vis++;
                    318:                }
                    319:        }
                    320:
1.3       kristaps  321:        /*
                    322:         * If we're not to right-marginalise it (newline), then instead
                    323:         * pad to the right margin and stay off.
                    324:         */
                    325:
                    326:        if (p->flags & TERMP_NOBREAK) {
                    327:                for ( ; vis <= maxvis; vis++)
                    328:                        putchar(' ');
                    329:        } else
                    330:                putchar('\n');
                    331:
1.2       kristaps  332:        p->col = 0;
                    333: }
                    334:
                    335:
                    336: static void
                    337: newln(struct termp *p)
                    338: {
                    339:
1.3       kristaps  340:        /*
                    341:         * A newline only breaks an existing line; it won't assert
                    342:         * vertical space.
                    343:         */
1.2       kristaps  344:        p->flags |= TERMP_NOSPACE;
                    345:        if (0 == p->col)
                    346:                return;
1.3       kristaps  347:        flushln(p);
1.2       kristaps  348: }
                    349:
                    350:
                    351: static void
                    352: vspace(struct termp *p)
                    353: {
                    354:
1.3       kristaps  355:        /*
                    356:         * Asserts a vertical space (a full, empty line-break between
                    357:         * lines).
                    358:         */
1.2       kristaps  359:        newln(p);
                    360:        putchar('\n');
                    361: }
                    362:
                    363:
                    364: static void
                    365: chara(struct termp *p, char c)
                    366: {
                    367:
1.3       kristaps  368:        /* TODO: dynamically expand the buffer. */
1.2       kristaps  369:        if (p->col + 1 >= p->maxcols)
                    370:                errx(1, "line overrun");
                    371:        p->buf[(p->col)++] = c;
                    372: }
                    373:
                    374:
                    375: static void
                    376: escape(struct termp *p, enum termesc esc)
                    377: {
                    378:
                    379:        if (p->col + 4 >= p->maxcols)
                    380:                errx(1, "line overrun");
                    381:
                    382:        p->buf[(p->col)++] = 27;
                    383:        p->buf[(p->col)++] = '[';
                    384:        switch (esc) {
                    385:        case (ESC_CLEAR):
                    386:                p->buf[(p->col)++] = '0';
                    387:                break;
                    388:        case (ESC_BOLD):
                    389:                p->buf[(p->col)++] = '1';
                    390:                break;
                    391:        case (ESC_UNDERLINE):
                    392:                p->buf[(p->col)++] = '4';
                    393:                break;
                    394:        default:
                    395:                abort();
                    396:                /* NOTREACHED */
                    397:        }
                    398:        p->buf[(p->col)++] = 'm';
                    399: }
                    400:
                    401:
                    402: static void
                    403: pword(struct termp *p, const char *word, size_t len)
                    404: {
                    405:        size_t           i;
                    406:
                    407:        assert(len > 0);
                    408:
                    409:        if ( ! (p->flags & TERMP_NOSPACE))
                    410:                chara(p, ' ');
                    411:
                    412:        p->flags &= ~TERMP_NOSPACE;
                    413:
                    414:        if (p->flags & TERMP_BOLD)
                    415:                escape(p, ESC_BOLD);
                    416:        if (p->flags & TERMP_UNDERLINE)
                    417:                escape(p, ESC_UNDERLINE);
                    418:
1.3       kristaps  419:        /* TODO: escape patterns. */
                    420:
1.2       kristaps  421:        for (i = 0; i < len; i++)
                    422:                chara(p, word[i]);
                    423:
                    424:        if (p->flags & TERMP_BOLD ||
                    425:                        p->flags & TERMP_UNDERLINE)
                    426:                escape(p, ESC_CLEAR);
                    427: }
                    428:
                    429:
                    430: static void
                    431: word(struct termp *p, const char *word)
                    432: {
                    433:        size_t           i, j, len;
                    434:
1.3       kristaps  435:        if (mdoc_isdelim(word))
                    436:                p->flags |= TERMP_NOSPACE;
1.2       kristaps  437:
                    438:        len = strlen(word);
                    439:        assert(len > 0);
                    440:
1.3       kristaps  441:        /* LINTED */
1.2       kristaps  442:        for (j = i = 0; i < len; i++) {
                    443:                if ( ! isspace(word[i])) {
                    444:                        j++;
                    445:                        continue;
                    446:                }
                    447:                if (0 == j)
                    448:                        continue;
                    449:                assert(i >= j);
                    450:                pword(p, &word[i - j], j);
                    451:                j = 0;
                    452:        }
                    453:        if (j > 0) {
                    454:                assert(i >= j);
                    455:                pword(p, &word[i - j], j);
                    456:        }
                    457: }
                    458:
                    459:
1.3       kristaps  460: /* ARGSUSED */
                    461: static int
                    462: termp_it_post(struct termp *p, const struct mdoc_meta *meta,
                    463:                const struct mdoc_node *node)
                    464: {
                    465:        const struct mdoc_node *n, *it;
                    466:        const struct mdoc_block *bl;
                    467:        int              i;
                    468:        size_t           width;
                    469:
                    470:        switch (node->type) {
                    471:        case (MDOC_BODY):
                    472:                /* FALLTHROUGH */
                    473:        case (MDOC_HEAD):
                    474:                break;
                    475:        default:
                    476:                return(1);
                    477:        }
                    478:
                    479:        it = node->parent;
                    480:        assert(MDOC_BLOCK == it->type);
                    481:        assert(MDOC_It == it->tok);
                    482:
                    483:        n = it->parent;
                    484:        assert(MDOC_BODY == n->type);
                    485:        assert(MDOC_Bl == n->tok);
                    486:        n = n->parent;
                    487:        bl = &n->data.block;
                    488:
                    489:        /* If `-tag', adjust our margins accordingly. */
                    490:
                    491:        if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
                    492:                i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
                    493:                assert(i >= 0);
                    494:                assert(1 == bl->argv[i].sz);
                    495:                width = strlen(*bl->argv[i].value); /* XXX */
                    496:
                    497:                if (MDOC_HEAD == node->type) {
                    498:                        flushln(p);
                    499:                        /* FIXME: nested lists. */
                    500:                        p->rmargin = p->maxrmargin;
                    501:                        p->flags &= ~TERMP_NOBREAK;
                    502:                } else {
                    503:                        flushln(p);
                    504:                        p->offset -= width + 1;
                    505:                        p->flags &= ~TERMP_NOLPAD;
                    506:                }
                    507:        }
                    508:
                    509:        return(1);
                    510: }
                    511:
                    512:
                    513: /* ARGSUSED */
1.2       kristaps  514: static int
                    515: termp_it_pre(struct termp *p, const struct mdoc_meta *meta,
                    516:                const struct mdoc_node *node)
                    517: {
1.3       kristaps  518:        const struct mdoc_node *n, *it;
                    519:        const struct mdoc_block *bl;
                    520:        int              i;
                    521:        size_t           width;
1.2       kristaps  522:
                    523:        switch (node->type) {
1.3       kristaps  524:        case (MDOC_BODY):
                    525:                /* FALLTHROUGH */
1.2       kristaps  526:        case (MDOC_HEAD):
1.3       kristaps  527:                it = node->parent;
                    528:                break;
                    529:        case (MDOC_BLOCK):
                    530:                it = node;
1.2       kristaps  531:                break;
1.1       kristaps  532:        default:
1.3       kristaps  533:                return(1);
                    534:        }
                    535:
                    536:        assert(MDOC_BLOCK == it->type);
                    537:        assert(MDOC_It == it->tok);
                    538:
                    539:        n = it->parent;
                    540:        assert(MDOC_BODY == n->type);
                    541:        assert(MDOC_Bl == n->tok);
                    542:        n = n->parent;
                    543:        bl = &n->data.block;
                    544:
                    545:        /* If `-compact', don't assert vertical space. */
                    546:
                    547:        if (MDOC_BLOCK == node->type) {
                    548:                if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
                    549:                        newln(p);
                    550:                else
                    551:                        vspace(p);
                    552:                return(1);
                    553:        }
                    554:
                    555:        assert(MDOC_HEAD == node->type
                    556:                        || MDOC_BODY == node->type);
                    557:
                    558:        /* If `-tag', adjust our margins accordingly. */
                    559:
                    560:        if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
                    561:                i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
                    562:                assert(i >= 0); /* XXX */
                    563:                assert(1 == bl->argv[i].sz);
                    564:                width = strlen(*bl->argv[i].value); /* XXX */
                    565:
                    566:                /* FIXME: nested lists. */
                    567:
                    568:                if (MDOC_HEAD == node->type) {
                    569:                        p->flags |= TERMP_NOBREAK;
                    570:                        p->flags |= TERMP_NOSPACE;
                    571:                        p->rmargin = p->offset + width;
                    572:                } else {
                    573:                        p->flags |= TERMP_NOSPACE;
                    574:                        p->flags |= TERMP_NOLPAD;
                    575:                        p->offset += width + 1;
                    576:                }
1.1       kristaps  577:        }
1.3       kristaps  578:
1.2       kristaps  579:        return(1);
                    580: }
1.1       kristaps  581:
1.2       kristaps  582:
1.3       kristaps  583: /* ARGSUSED */
1.2       kristaps  584: static int
                    585: termp_bold_post(struct termp *p, const struct mdoc_meta *meta,
                    586:                const struct mdoc_node *node)
                    587: {
                    588:
                    589:        p->flags &= ~TERMP_BOLD;
                    590:        return(1);
                    591: }
                    592:
                    593:
1.3       kristaps  594: /* ARGSUSED */
1.2       kristaps  595: static int
                    596: termp_under_pre(struct termp *p, const struct mdoc_meta *meta,
                    597:                const struct mdoc_node *node)
                    598: {
                    599:
                    600:        p->flags |= TERMP_UNDERLINE;
                    601:        return(1);
1.1       kristaps  602: }
                    603:
                    604:
1.3       kristaps  605: /* ARGSUSED */
1.2       kristaps  606: static int
                    607: termp_bold_pre(struct termp *p, const struct mdoc_meta *meta,
                    608:                const struct mdoc_node *node)
1.1       kristaps  609: {
                    610:
1.2       kristaps  611:        p->flags |= TERMP_BOLD;
                    612:        return(1);
                    613: }
                    614:
                    615:
1.3       kristaps  616: /* ARGSUSED */
1.2       kristaps  617: static int
                    618: termp_ns_pre(struct termp *p, const struct mdoc_meta *meta,
                    619:                const struct mdoc_node *node)
                    620: {
                    621:
                    622:        p->flags |= TERMP_NOSPACE;
                    623:        return(1);
                    624: }
                    625:
                    626:
1.3       kristaps  627: /* ARGSUSED */
1.2       kristaps  628: static int
                    629: termp_pp_pre(struct termp *p, const struct mdoc_meta *meta,
                    630:                const struct mdoc_node *node)
                    631: {
                    632:
                    633:        vspace(p);
                    634:        return(1);
                    635: }
                    636:
                    637:
1.3       kristaps  638: /* ARGSUSED */
1.2       kristaps  639: static int
                    640: termp_under_post(struct termp *p, const struct mdoc_meta *meta,
                    641:                const struct mdoc_node *node)
                    642: {
                    643:
                    644:        p->flags &= ~TERMP_UNDERLINE;
                    645:        return(1);
                    646: }
                    647:
                    648:
1.3       kristaps  649: /* ARGSUSED */
1.2       kristaps  650: static int
                    651: termp_nd_pre(struct termp *p, const struct mdoc_meta *meta,
                    652:                const struct mdoc_node *node)
                    653: {
                    654:
                    655:        word(p, "-");
                    656:        return(1);
                    657: }
                    658:
                    659:
1.3       kristaps  660: /* ARGSUSED */
1.2       kristaps  661: static int
                    662: termp_bl_post(struct termp *p, const struct mdoc_meta *meta,
                    663:                const struct mdoc_node *node)
                    664: {
                    665:
                    666:        switch (node->type) {
                    667:        case (MDOC_BLOCK):
                    668:                newln(p);
                    669:                break;
1.1       kristaps  670:        default:
                    671:                break;
                    672:        }
1.2       kristaps  673:        return(1);
1.1       kristaps  674: }
                    675:
                    676:
1.3       kristaps  677: /* ARGSUSED */
1.2       kristaps  678: static int
                    679: termp_op_post(struct termp *p, const struct mdoc_meta *meta,
                    680:                const struct mdoc_node *node)
                    681: {
                    682:
                    683:        switch (node->type) {
                    684:        case (MDOC_BODY):
                    685:                p->flags |= TERMP_NOSPACE;
1.4     ! kristaps  686:                word(p, "\\]");
1.2       kristaps  687:                break;
                    688:        default:
                    689:                break;
                    690:        }
                    691:        return(1);
                    692: }
                    693:
                    694:
1.3       kristaps  695: /* ARGSUSED */
1.2       kristaps  696: static int
                    697: termp_sh_post(struct termp *p, const struct mdoc_meta *meta,
                    698:                const struct mdoc_node *node)
1.1       kristaps  699: {
                    700:
1.2       kristaps  701:        switch (node->type) {
                    702:        case (MDOC_HEAD):
                    703:                p->flags &= ~TERMP_BOLD;
                    704:                newln(p);
                    705:                break;
                    706:        case (MDOC_BODY):
                    707:                newln(p);
1.3       kristaps  708:                p->offset -= 4;
1.2       kristaps  709:                break;
                    710:        default:
                    711:                break;
                    712:        }
                    713:        return(1);
                    714: }
                    715:
1.1       kristaps  716:
1.3       kristaps  717: /* ARGSUSED */
1.2       kristaps  718: static int
1.4     ! kristaps  719: termp_xr_pre(struct termp *p, const struct mdoc_meta *meta,
        !           720:                const struct mdoc_node *node)
        !           721: {
        !           722:        const struct mdoc_node *n;
        !           723:
        !           724:        n = node->child;
        !           725:        assert(n);
        !           726:
        !           727:        assert(MDOC_TEXT == n->type);
        !           728:        word(p, n->data.text.string);
        !           729:
        !           730:        if (NULL == (n = n->next))
        !           731:                return(0);
        !           732:
        !           733:        assert(MDOC_TEXT == n->type);
        !           734:        p->flags |= TERMP_NOSPACE;
        !           735:        word(p, "\\(");
        !           736:        p->flags |= TERMP_NOSPACE;
        !           737:        word(p, n->data.text.string);
        !           738:        p->flags |= TERMP_NOSPACE;
        !           739:        word(p, "\\)");
        !           740:
        !           741:        return(0);
        !           742: }
        !           743:
        !           744:
        !           745: /* ARGSUSED */
        !           746: static int
1.2       kristaps  747: termp_sh_pre(struct termp *p, const struct mdoc_meta *meta,
                    748:                const struct mdoc_node *node)
                    749: {
1.1       kristaps  750:
1.2       kristaps  751:        switch (node->type) {
                    752:        case (MDOC_HEAD):
                    753:                vspace(p);
                    754:                p->flags |= TERMP_BOLD;
                    755:                break;
                    756:        case (MDOC_BODY):
1.3       kristaps  757:                p->offset += 4;
1.2       kristaps  758:                break;
                    759:        default:
                    760:                break;
1.1       kristaps  761:        }
1.2       kristaps  762:        return(1);
                    763: }
                    764:
1.1       kristaps  765:
1.3       kristaps  766: /* ARGSUSED */
1.2       kristaps  767: static int
                    768: termp_op_pre(struct termp *p, const struct mdoc_meta *meta,
                    769:                const struct mdoc_node *node)
                    770: {
                    771:
                    772:        switch (node->type) {
                    773:        case (MDOC_BODY):
1.4     ! kristaps  774:                word(p, "\\[");
1.2       kristaps  775:                p->flags |= TERMP_NOSPACE;
                    776:                break;
                    777:        default:
                    778:                break;
                    779:        }
                    780:        return(1);
1.1       kristaps  781: }
                    782:
                    783:
1.3       kristaps  784: /* ARGSUSED */
1.1       kristaps  785: static int
1.2       kristaps  786: termp_fl_pre(struct termp *p, const struct mdoc_meta *meta,
                    787:                const struct mdoc_node *node)
1.1       kristaps  788: {
                    789:
1.2       kristaps  790:        p->flags |= TERMP_BOLD;
                    791:        word(p, "-");
                    792:        p->flags |= TERMP_NOSPACE;
1.1       kristaps  793:        return(1);
                    794: }
                    795:
                    796:
                    797: static void
1.2       kristaps  798: termprint_r(struct termp *p, const struct mdoc_meta *meta,
                    799:                const struct mdoc_node *node)
                    800: {
1.4     ! kristaps  801:        int              dochild;
1.2       kristaps  802:
                    803:        /* Pre-processing ----------------- */
                    804:
1.4     ! kristaps  805:        dochild = 1;
        !           806:
1.2       kristaps  807:        if (MDOC_TEXT != node->type) {
                    808:                if (termacts[node->tok].pre)
                    809:                        if ( ! (*termacts[node->tok].pre)(p, meta, node))
1.4     ! kristaps  810:                                dochild = 0;
1.2       kristaps  811:        } else /* MDOC_TEXT == node->type */
                    812:                word(p, node->data.text.string);
                    813:
                    814:        /* Children ---------------------- */
                    815:
1.4     ! kristaps  816:        if (dochild && NULL == node->child) {
1.2       kristaps  817:                /* No-child processing. */
                    818:                switch (node->type) {
                    819:                case (MDOC_ELEM):
                    820:                        switch (node->tok) {
                    821:                        case (MDOC_Nm):
                    822:                                word(p, "progname"); /* TODO */
                    823:                                break;
                    824:                        case (MDOC_Ar):
                    825:                                word(p, "...");
                    826:                                break;
                    827:                        default:
                    828:                                break;
                    829:                        }
                    830:                        break;
                    831:                default:
                    832:                        break;
                    833:                }
1.4     ! kristaps  834:        } else if (dochild)
1.2       kristaps  835:                termprint_r(p, meta, node->child);
                    836:
                    837:        /* Post-processing --------------- */
                    838:
                    839:        if (MDOC_TEXT != node->type) {
                    840:                if (termacts[node->tok].post)
                    841:                        if ( ! (*termacts[node->tok].post)(p, meta, node))
                    842:                                return;
                    843:        }
                    844:
                    845:        /* Siblings ---------------------- */
                    846:
                    847:        if (node->next)
                    848:                termprint_r(p, meta, node->next);
                    849: }
                    850:
                    851:
                    852: static void
                    853: termprint_footer(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  854: {
                    855:        struct tm       *tm;
                    856:        char            *buf, *os;
                    857:        size_t           sz, osz, ssz, i;
                    858:
1.3       kristaps  859:        if (NULL == (buf = malloc(p->rmargin)))
1.1       kristaps  860:                err(1, "malloc");
1.3       kristaps  861:        if (NULL == (os = malloc(p->rmargin)))
1.1       kristaps  862:                err(1, "malloc");
                    863:
                    864:        tm = localtime(&meta->date);
1.3       kristaps  865:        if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
1.1       kristaps  866:                err(1, "strftime");
                    867:
1.3       kristaps  868:        osz = strlcpy(os, meta->os, p->rmargin);
1.1       kristaps  869:
                    870:        sz = strlen(buf);
                    871:        ssz = sz + osz + 1;
                    872:
1.3       kristaps  873:        if (ssz > p->rmargin) {
                    874:                ssz -= p->rmargin;
1.1       kristaps  875:                assert(ssz <= osz);
                    876:                os[osz - ssz] = 0;
                    877:                ssz = 1;
                    878:        } else
1.3       kristaps  879:                ssz = p->rmargin - ssz + 1;
1.1       kristaps  880:
1.2       kristaps  881:        printf("\n");
1.1       kristaps  882:        printf("%s", os);
                    883:        for (i = 0; i < ssz; i++)
                    884:                printf(" ");
                    885:
                    886:        printf("%s\n", buf);
1.2       kristaps  887:        fflush(stdout);
1.1       kristaps  888:
                    889:        free(buf);
                    890:        free(os);
                    891: }
                    892:
                    893:
                    894: static void
1.2       kristaps  895: termprint_header(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  896: {
1.2       kristaps  897:        char            *msec, *buf, *title, *pp;
                    898:        size_t           ssz, tsz, ttsz, i;;
1.1       kristaps  899:
1.3       kristaps  900:        if (NULL == (buf = malloc(p->rmargin)))
1.1       kristaps  901:                err(1, "malloc");
1.3       kristaps  902:        if (NULL == (title = malloc(p->rmargin)))
1.1       kristaps  903:                err(1, "malloc");
                    904:
1.2       kristaps  905:        if (NULL == (pp = mdoc_vol2a(meta->vol)))
                    906:                switch (meta->msec) {
                    907:                case (MSEC_1):
                    908:                        /* FALLTHROUGH */
                    909:                case (MSEC_6):
                    910:                        /* FALLTHROUGH */
                    911:                case (MSEC_7):
                    912:                        pp = mdoc_vol2a(VOL_URM);
                    913:                        break;
                    914:                case (MSEC_8):
                    915:                        pp = mdoc_vol2a(VOL_SMM);
                    916:                        break;
                    917:                case (MSEC_2):
                    918:                        /* FALLTHROUGH */
                    919:                case (MSEC_3):
                    920:                        /* FALLTHROUGH */
                    921:                case (MSEC_4):
                    922:                        /* FALLTHROUGH */
                    923:                case (MSEC_5):
                    924:                        pp = mdoc_vol2a(VOL_PRM);
                    925:                        break;
                    926:                case (MSEC_9):
                    927:                        pp = mdoc_vol2a(VOL_KM);
                    928:                        break;
                    929:                default:
                    930:                        /* FIXME: capitalise. */
                    931:                        if (NULL == (pp = mdoc_msec2a(meta->msec)))
                    932:                                pp = mdoc_msec2a(MSEC_local);
                    933:                        break;
                    934:                }
                    935:        assert(pp);
1.1       kristaps  936:
1.3       kristaps  937:        tsz = strlcpy(buf, pp, p->rmargin);
                    938:        assert(tsz < p->rmargin);
1.1       kristaps  939:
1.2       kristaps  940:        if ((pp = mdoc_arch2a(meta->arch))) {
1.3       kristaps  941:                tsz = strlcat(buf, " (", p->rmargin);
                    942:                assert(tsz < p->rmargin);
                    943:                tsz = strlcat(buf, pp, p->rmargin);
                    944:                assert(tsz < p->rmargin);
                    945:                tsz = strlcat(buf, ")", p->rmargin);
                    946:                assert(tsz < p->rmargin);
1.2       kristaps  947:        }
                    948:
1.3       kristaps  949:        ttsz = strlcpy(title, meta->title, p->rmargin);
1.2       kristaps  950:
                    951:        if (NULL == (msec = mdoc_msec2a(meta->msec)))
1.1       kristaps  952:                msec = "";
                    953:
                    954:        ssz = (2 * (ttsz + 2 + strlen(msec))) + tsz + 2;
                    955:
1.3       kristaps  956:        if (ssz > p->rmargin) {
                    957:                if ((ssz -= p->rmargin) % 2)
1.1       kristaps  958:                        ssz++;
                    959:                ssz /= 2;
                    960:
                    961:                assert(ssz <= ttsz);
                    962:                title[ttsz - ssz] = 0;
                    963:                ssz = 1;
                    964:        } else
1.3       kristaps  965:                ssz = ((p->rmargin - ssz) / 2) + 1;
1.1       kristaps  966:
                    967:        printf("%s(%s)", title, msec);
                    968:
                    969:        for (i = 0; i < ssz; i++)
                    970:                printf(" ");
                    971:
                    972:        printf("%s", buf);
                    973:
                    974:        for (i = 0; i < ssz; i++)
                    975:                printf(" ");
                    976:
1.2       kristaps  977:        printf("%s(%s)\n", title, msec);
                    978:        fflush(stdout);
1.1       kristaps  979:
                    980:        free(title);
                    981:        free(buf);
                    982: }
                    983:
                    984:
                    985: int
                    986: termprint(const struct mdoc_node *node,
                    987:                const struct mdoc_meta *meta)
                    988: {
1.2       kristaps  989:        struct termp     p;
1.1       kristaps  990:
                    991:        if (ERR == setupterm(NULL, STDOUT_FILENO, NULL))
                    992:                return(0);
                    993:
1.3       kristaps  994:        p.maxrmargin = columns < 60 ? 60 : (size_t)columns;
                    995:        p.rmargin = p.maxrmargin;
1.2       kristaps  996:        p.maxcols = 1024;
1.3       kristaps  997:        p.offset = p.col = 0;
1.2       kristaps  998:        p.flags = TERMP_NOSPACE;
                    999:
                   1000:        if (NULL == (p.buf = malloc(p.maxcols)))
                   1001:                err(1, "malloc");
                   1002:
                   1003:        termprint_header(&p, meta);
                   1004:        termprint_r(&p, meta, node);
                   1005:        termprint_footer(&p, meta);
                   1006:
                   1007:        free(p.buf);
1.1       kristaps 1008:
                   1009:        return(1);
                   1010: }
                   1011:
                   1012:

CVSweb