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

Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.13

1.13    ! schwarze    1: /*     $Id: man_term.c,v 1.12 2009/08/22 20:19:24 schwarze Exp $ */
1.1       kristaps    2: /*
1.2       schwarze    3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1       kristaps    4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
1.2       schwarze    6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    8:  *
1.2       schwarze    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.
1.1       kristaps   16:  */
1.13    ! schwarze   17: #include <sys/types.h>
        !            18:
1.1       kristaps   19: #include <assert.h>
1.11      schwarze   20: #include <ctype.h>
1.1       kristaps   21: #include <err.h>
                     22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
                     26: #include "term.h"
                     27: #include "man.h"
1.10      schwarze   28:
                     29: #define        INDENT            7
                     30: #define        HALFINDENT        3
1.1       kristaps   31:
1.11      schwarze   32: struct mtermp {
                     33:        int               fl;
                     34: #define        MANT_LITERAL     (1 << 0)
1.13    ! schwarze   35:        /*
        !            36:         * Default amount to indent the left margin after leading text
        !            37:         * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
        !            38:         * indent).  This needs to be saved because `HP' and so on, if
        !            39:         * not having a specified value, must default.
        !            40:         *
        !            41:         * Note that this is the indentation AFTER the left offset, so
        !            42:         * the total offset is usually offset + lmargin.
        !            43:         */
        !            44:        size_t            lmargin;
        !            45:        /*
        !            46:         * The default offset, i.e., the amount between any text and the
        !            47:         * page boundary.
        !            48:         */
        !            49:        size_t            offset;
1.11      schwarze   50: };
                     51:
1.1       kristaps   52: #define        DECL_ARGS         struct termp *p, \
1.11      schwarze   53:                          struct mtermp *mt, \
1.1       kristaps   54:                          const struct man_node *n, \
                     55:                          const struct man_meta *m
                     56:
                     57: struct termact {
                     58:        int             (*pre)(DECL_ARGS);
                     59:        void            (*post)(DECL_ARGS);
                     60: };
                     61:
                     62: static int               pre_B(DECL_ARGS);
                     63: static int               pre_BI(DECL_ARGS);
                     64: static int               pre_BR(DECL_ARGS);
1.11      schwarze   65: static int               pre_HP(DECL_ARGS);
1.1       kristaps   66: static int               pre_I(DECL_ARGS);
                     67: static int               pre_IB(DECL_ARGS);
                     68: static int               pre_IP(DECL_ARGS);
                     69: static int               pre_IR(DECL_ARGS);
                     70: static int               pre_PP(DECL_ARGS);
                     71: static int               pre_RB(DECL_ARGS);
                     72: static int               pre_RI(DECL_ARGS);
1.13    ! schwarze   73: static int               pre_RS(DECL_ARGS);
1.1       kristaps   74: static int               pre_SH(DECL_ARGS);
                     75: static int               pre_SS(DECL_ARGS);
                     76: static int               pre_TP(DECL_ARGS);
1.11      schwarze   77: static int               pre_br(DECL_ARGS);
                     78: static int               pre_fi(DECL_ARGS);
                     79: static int               pre_nf(DECL_ARGS);
                     80: static int               pre_r(DECL_ARGS);
                     81: static int               pre_sp(DECL_ARGS);
1.1       kristaps   82:
                     83: static void              post_B(DECL_ARGS);
                     84: static void              post_I(DECL_ARGS);
1.11      schwarze   85: static void              post_IP(DECL_ARGS);
                     86: static void              post_HP(DECL_ARGS);
1.13    ! schwarze   87: static void              post_RS(DECL_ARGS);
1.1       kristaps   88: static void              post_SH(DECL_ARGS);
                     89: static void              post_SS(DECL_ARGS);
1.11      schwarze   90: static void              post_TP(DECL_ARGS);
                     91: static void              post_i(DECL_ARGS);
1.1       kristaps   92:
                     93: static const struct termact termacts[MAN_MAX] = {
1.8       schwarze   94:        { pre_br, NULL }, /* br */
1.1       kristaps   95:        { NULL, NULL }, /* TH */
                     96:        { pre_SH, post_SH }, /* SH */
                     97:        { pre_SS, post_SS }, /* SS */
1.11      schwarze   98:        { pre_TP, post_TP }, /* TP */
1.1       kristaps   99:        { pre_PP, NULL }, /* LP */
                    100:        { pre_PP, NULL }, /* PP */
                    101:        { pre_PP, NULL }, /* P */
1.11      schwarze  102:        { pre_IP, post_IP }, /* IP */
                    103:        { pre_HP, post_HP }, /* HP */
1.1       kristaps  104:        { NULL, NULL }, /* SM */
                    105:        { pre_B, post_B }, /* SB */
                    106:        { pre_BI, NULL }, /* BI */
                    107:        { pre_IB, NULL }, /* IB */
                    108:        { pre_BR, NULL }, /* BR */
                    109:        { pre_RB, NULL }, /* RB */
                    110:        { NULL, NULL }, /* R */
                    111:        { pre_B, post_B }, /* B */
                    112:        { pre_I, post_I }, /* I */
                    113:        { pre_IR, NULL }, /* IR */
                    114:        { pre_RI, NULL }, /* RI */
1.13    ! schwarze  115:        { NULL, NULL }, /* na */
1.11      schwarze  116:        { pre_I, post_i }, /* i */
                    117:        { pre_sp, NULL }, /* sp */
                    118:        { pre_nf, NULL }, /* nf */
                    119:        { pre_fi, NULL }, /* fi */
                    120:        { pre_r, NULL }, /* r */
1.13    ! schwarze  121:        { NULL, NULL }, /* RE */
        !           122:        { pre_RS, post_RS }, /* RS */
        !           123:        { NULL, NULL }, /* DT */
1.1       kristaps  124: };
1.11      schwarze  125:
1.1       kristaps  126: static void              print_head(struct termp *,
                    127:                                const struct man_meta *);
                    128: static void              print_body(DECL_ARGS);
                    129: static void              print_node(DECL_ARGS);
                    130: static void              print_foot(struct termp *,
                    131:                                const struct man_meta *);
1.11      schwarze  132: static void              fmt_block_vspace(struct termp *,
                    133:                                const struct man_node *);
                    134: static int               arg_width(const struct man_node *);
1.1       kristaps  135:
                    136:
                    137: int
                    138: man_run(struct termp *p, const struct man *m)
                    139: {
1.11      schwarze  140:        struct mtermp    mt;
1.1       kristaps  141:
                    142:        print_head(p, man_meta(m));
                    143:        p->flags |= TERMP_NOSPACE;
1.7       schwarze  144:        assert(man_node(m));
                    145:        assert(MAN_ROOT == man_node(m)->type);
1.11      schwarze  146:
                    147:        mt.fl = 0;
                    148:        mt.lmargin = INDENT;
1.13    ! schwarze  149:        mt.offset = INDENT;
1.11      schwarze  150:
1.7       schwarze  151:        if (man_node(m)->child)
1.11      schwarze  152:                print_body(p, &mt, man_node(m)->child, man_meta(m));
1.1       kristaps  153:        print_foot(p, man_meta(m));
                    154:
                    155:        return(1);
                    156: }
                    157:
                    158:
1.11      schwarze  159: static void
                    160: fmt_block_vspace(struct termp *p, const struct man_node *n)
                    161: {
                    162:        term_newln(p);
                    163:
                    164:        if (NULL == n->prev)
                    165:                return;
                    166:
                    167:        if (MAN_SS == n->prev->tok)
                    168:                return;
                    169:        if (MAN_SH == n->prev->tok)
                    170:                return;
                    171:
                    172:        term_vspace(p);
                    173: }
                    174:
                    175:
                    176: static int
                    177: arg_width(const struct man_node *n)
                    178: {
                    179:        int              i, len;
                    180:        const char      *p;
                    181:
                    182:        assert(MAN_TEXT == n->type);
                    183:        assert(n->string);
                    184:
                    185:        p = n->string;
                    186:
                    187:        if (0 == (len = (int)strlen(p)))
                    188:                return(-1);
                    189:
                    190:        for (i = 0; i < len; i++)
                    191:                if ( ! isdigit((u_char)p[i]))
                    192:                        break;
                    193:
                    194:        if (i == len - 1)  {
                    195:                if ('n' == p[len - 1] || 'm' == p[len - 1])
                    196:                        return(atoi(p));
                    197:        } else if (i == len)
                    198:                return(atoi(p));
                    199:
                    200:        return(-1);
                    201: }
                    202:
                    203:
1.1       kristaps  204: /* ARGSUSED */
                    205: static int
                    206: pre_I(DECL_ARGS)
                    207: {
                    208:
                    209:        p->flags |= TERMP_UNDER;
                    210:        return(1);
                    211: }
                    212:
                    213:
                    214: /* ARGSUSED */
1.11      schwarze  215: static int
                    216: pre_r(DECL_ARGS)
                    217: {
                    218:
                    219:        p->flags &= ~TERMP_UNDER;
                    220:        p->flags &= ~TERMP_BOLD;
                    221:        return(1);
                    222: }
                    223:
                    224:
                    225: /* ARGSUSED */
                    226: static void
                    227: post_i(DECL_ARGS)
                    228: {
                    229:
                    230:        if (n->nchild)
                    231:                p->flags &= ~TERMP_UNDER;
                    232: }
                    233:
                    234:
                    235: /* ARGSUSED */
1.1       kristaps  236: static void
                    237: post_I(DECL_ARGS)
                    238: {
                    239:
                    240:        p->flags &= ~TERMP_UNDER;
                    241: }
                    242:
                    243:
                    244: /* ARGSUSED */
                    245: static int
1.11      schwarze  246: pre_fi(DECL_ARGS)
                    247: {
                    248:
                    249:        mt->fl &= ~MANT_LITERAL;
                    250:        return(1);
                    251: }
                    252:
                    253:
                    254: /* ARGSUSED */
                    255: static int
                    256: pre_nf(DECL_ARGS)
                    257: {
                    258:
                    259:        term_newln(p);
                    260:        mt->fl |= MANT_LITERAL;
                    261:        return(1);
                    262: }
                    263:
                    264:
                    265: /* ARGSUSED */
                    266: static int
1.1       kristaps  267: pre_IR(DECL_ARGS)
                    268: {
                    269:        const struct man_node *nn;
                    270:        int              i;
                    271:
                    272:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    273:                if ( ! (i % 2))
                    274:                        p->flags |= TERMP_UNDER;
                    275:                if (i > 0)
                    276:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  277:                print_node(p, mt, nn, m);
1.1       kristaps  278:                if ( ! (i % 2))
                    279:                        p->flags &= ~TERMP_UNDER;
                    280:        }
                    281:        return(0);
                    282: }
                    283:
                    284:
                    285: /* ARGSUSED */
                    286: static int
                    287: pre_IB(DECL_ARGS)
                    288: {
                    289:        const struct man_node *nn;
                    290:        int              i;
                    291:
                    292:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    293:                p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
                    294:                if (i > 0)
                    295:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  296:                print_node(p, mt, nn, m);
1.1       kristaps  297:                p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
                    298:        }
                    299:        return(0);
                    300: }
                    301:
                    302:
                    303: /* ARGSUSED */
                    304: static int
                    305: pre_RB(DECL_ARGS)
                    306: {
                    307:        const struct man_node *nn;
                    308:        int              i;
                    309:
                    310:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    311:                if (i % 2)
                    312:                        p->flags |= TERMP_BOLD;
                    313:                if (i > 0)
                    314:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  315:                print_node(p, mt, nn, m);
1.1       kristaps  316:                if (i % 2)
                    317:                        p->flags &= ~TERMP_BOLD;
                    318:        }
                    319:        return(0);
                    320: }
                    321:
                    322:
                    323: /* ARGSUSED */
                    324: static int
                    325: pre_RI(DECL_ARGS)
                    326: {
                    327:        const struct man_node *nn;
                    328:        int              i;
                    329:
                    330:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    331:                if ( ! (i % 2))
                    332:                        p->flags |= TERMP_UNDER;
                    333:                if (i > 0)
                    334:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  335:                print_node(p, mt, nn, m);
1.1       kristaps  336:                if ( ! (i % 2))
                    337:                        p->flags &= ~TERMP_UNDER;
                    338:        }
                    339:        return(0);
                    340: }
                    341:
                    342:
                    343: /* ARGSUSED */
                    344: static int
                    345: pre_BR(DECL_ARGS)
                    346: {
                    347:        const struct man_node *nn;
                    348:        int              i;
                    349:
                    350:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    351:                if ( ! (i % 2))
                    352:                        p->flags |= TERMP_BOLD;
                    353:                if (i > 0)
                    354:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  355:                print_node(p, mt, nn, m);
1.1       kristaps  356:                if ( ! (i % 2))
                    357:                        p->flags &= ~TERMP_BOLD;
                    358:        }
                    359:        return(0);
                    360: }
                    361:
                    362:
                    363: /* ARGSUSED */
                    364: static int
                    365: pre_BI(DECL_ARGS)
                    366: {
                    367:        const struct man_node *nn;
                    368:        int              i;
                    369:
                    370:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    371:                p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
                    372:                if (i > 0)
                    373:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  374:                print_node(p, mt, nn, m);
1.1       kristaps  375:                p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
                    376:        }
                    377:        return(0);
                    378: }
                    379:
                    380:
                    381: /* ARGSUSED */
                    382: static int
                    383: pre_B(DECL_ARGS)
                    384: {
                    385:
                    386:        p->flags |= TERMP_BOLD;
                    387:        return(1);
                    388: }
                    389:
                    390:
                    391: /* ARGSUSED */
                    392: static void
                    393: post_B(DECL_ARGS)
                    394: {
                    395:
                    396:        p->flags &= ~TERMP_BOLD;
                    397: }
                    398:
                    399:
                    400: /* ARGSUSED */
                    401: static int
1.11      schwarze  402: pre_sp(DECL_ARGS)
                    403: {
                    404:        int              i, len;
                    405:
                    406:        if (NULL == n->child) {
                    407:                term_vspace(p);
                    408:                return(0);
                    409:        }
                    410:
                    411:        len = atoi(n->child->string);
                    412:        if (0 == len)
                    413:                term_newln(p);
                    414:        for (i = 0; i < len; i++)
                    415:                term_vspace(p);
                    416:
                    417:        return(0);
                    418: }
                    419:
                    420:
                    421: /* ARGSUSED */
                    422: static int
1.8       schwarze  423: pre_br(DECL_ARGS)
                    424: {
                    425:
                    426:        term_newln(p);
                    427:        return(0);
                    428: }
                    429:
                    430:
                    431: /* ARGSUSED */
                    432: static int
1.11      schwarze  433: pre_HP(DECL_ARGS)
                    434: {
                    435:        size_t                   len;
                    436:        int                      ival;
                    437:        const struct man_node   *nn;
                    438:
                    439:        switch (n->type) {
                    440:        case (MAN_BLOCK):
                    441:                fmt_block_vspace(p, n);
                    442:                return(1);
                    443:        case (MAN_BODY):
                    444:                p->flags |= TERMP_NOBREAK;
                    445:                p->flags |= TERMP_TWOSPACE;
                    446:                break;
                    447:        default:
                    448:                return(0);
                    449:        }
                    450:
1.13    ! schwarze  451:        len = mt->lmargin;
1.11      schwarze  452:        ival = -1;
                    453:
                    454:        /* Calculate offset. */
                    455:
                    456:        if (NULL != (nn = n->parent->head->child))
                    457:                if ((ival = arg_width(nn)) >= 0)
                    458:                        len = (size_t)ival;
                    459:
                    460:        if (0 == len)
                    461:                len = 1;
                    462:
1.13    ! schwarze  463:        p->offset = mt->offset;
        !           464:        p->rmargin = mt->offset + len;
1.11      schwarze  465:
                    466:        if (ival >= 0)
1.13    ! schwarze  467:                mt->lmargin = (size_t)ival;
1.11      schwarze  468:
                    469:        return(1);
                    470: }
                    471:
                    472:
                    473: /* ARGSUSED */
                    474: static void
                    475: post_HP(DECL_ARGS)
                    476: {
                    477:
                    478:        switch (n->type) {
                    479:        case (MAN_BLOCK):
                    480:                term_flushln(p);
                    481:                break;
                    482:        case (MAN_BODY):
                    483:                term_flushln(p);
                    484:                p->flags &= ~TERMP_NOBREAK;
                    485:                p->flags &= ~TERMP_TWOSPACE;
1.13    ! schwarze  486:                p->offset = mt->offset;
1.11      schwarze  487:                p->rmargin = p->maxrmargin;
                    488:                break;
                    489:        default:
                    490:                break;
                    491:        }
                    492: }
                    493:
                    494:
                    495: /* ARGSUSED */
                    496: static int
1.1       kristaps  497: pre_PP(DECL_ARGS)
                    498: {
                    499:
1.11      schwarze  500:        switch (n->type) {
                    501:        case (MAN_BLOCK):
                    502:                mt->lmargin = INDENT;
                    503:                fmt_block_vspace(p, n);
                    504:                break;
                    505:        default:
1.13    ! schwarze  506:                p->offset = mt->offset;
1.11      schwarze  507:                break;
                    508:        }
                    509:
                    510:        return(1);
1.1       kristaps  511: }
                    512:
                    513:
                    514: /* ARGSUSED */
                    515: static int
                    516: pre_IP(DECL_ARGS)
                    517: {
1.11      schwarze  518:        const struct man_node   *nn;
                    519:        size_t                   len;
                    520:        int                      ival;
                    521:
                    522:        switch (n->type) {
                    523:        case (MAN_BODY):
                    524:                p->flags |= TERMP_NOLPAD;
                    525:                p->flags |= TERMP_NOSPACE;
                    526:                break;
                    527:        case (MAN_HEAD):
                    528:                p->flags |= TERMP_NOBREAK;
                    529:                p->flags |= TERMP_TWOSPACE;
                    530:                break;
                    531:        case (MAN_BLOCK):
                    532:                fmt_block_vspace(p, n);
                    533:                /* FALLTHROUGH */
                    534:        default:
                    535:                return(1);
                    536:        }
                    537:
1.13    ! schwarze  538:        len = mt->lmargin;
1.11      schwarze  539:        ival = -1;
                    540:
                    541:        /* Calculate offset. */
                    542:
                    543:        if (NULL != (nn = n->parent->head->child))
                    544:                if (NULL != (nn = nn->next)) {
                    545:                        for ( ; nn->next; nn = nn->next)
                    546:                                /* Do nothing. */ ;
                    547:                        if ((ival = arg_width(nn)) >= 0)
                    548:                                len = (size_t)ival;
                    549:                }
                    550:
                    551:        switch (n->type) {
                    552:        case (MAN_HEAD):
                    553:                /* Handle zero-width lengths. */
                    554:                if (0 == len)
                    555:                        len = 1;
                    556:
1.13    ! schwarze  557:                p->offset = mt->offset;
        !           558:                p->rmargin = mt->offset + len;
1.11      schwarze  559:                if (ival < 0)
                    560:                        break;
                    561:
                    562:                /* Set the saved left-margin. */
1.13    ! schwarze  563:                mt->lmargin = (size_t)ival;
1.1       kristaps  564:
1.11      schwarze  565:                /* Don't print the length value. */
                    566:                for (nn = n->child; nn->next; nn = nn->next)
                    567:                        print_node(p, mt, nn, m);
                    568:                return(0);
                    569:        case (MAN_BODY):
1.13    ! schwarze  570:                p->offset = mt->offset + len;
1.11      schwarze  571:                p->rmargin = p->maxrmargin;
                    572:                break;
                    573:        default:
                    574:                break;
                    575:        }
1.1       kristaps  576:
1.11      schwarze  577:        return(1);
                    578: }
1.1       kristaps  579:
                    580:
1.11      schwarze  581: /* ARGSUSED */
                    582: static void
                    583: post_IP(DECL_ARGS)
                    584: {
1.4       schwarze  585:
1.11      schwarze  586:        switch (n->type) {
                    587:        case (MAN_HEAD):
                    588:                term_flushln(p);
                    589:                p->flags &= ~TERMP_NOBREAK;
                    590:                p->flags &= ~TERMP_TWOSPACE;
                    591:                p->rmargin = p->maxrmargin;
                    592:                break;
                    593:        case (MAN_BODY):
                    594:                term_flushln(p);
                    595:                p->flags &= ~TERMP_NOLPAD;
                    596:                break;
                    597:        default:
                    598:                break;
                    599:        }
1.1       kristaps  600: }
                    601:
                    602:
                    603: /* ARGSUSED */
                    604: static int
                    605: pre_TP(DECL_ARGS)
                    606: {
1.11      schwarze  607:        const struct man_node   *nn;
                    608:        size_t                   len;
                    609:        int                      ival;
                    610:
                    611:        switch (n->type) {
                    612:        case (MAN_HEAD):
                    613:                p->flags |= TERMP_NOBREAK;
                    614:                p->flags |= TERMP_TWOSPACE;
                    615:                break;
                    616:        case (MAN_BODY):
                    617:                p->flags |= TERMP_NOLPAD;
                    618:                p->flags |= TERMP_NOSPACE;
                    619:                break;
                    620:        case (MAN_BLOCK):
                    621:                fmt_block_vspace(p, n);
                    622:                /* FALLTHROUGH */
                    623:        default:
                    624:                return(1);
                    625:        }
                    626:
                    627:        len = (size_t)mt->lmargin;
                    628:        ival = -1;
                    629:
                    630:        /* Calculate offset. */
1.1       kristaps  631:
1.11      schwarze  632:        if (NULL != (nn = n->parent->head->child))
                    633:                if (NULL != nn->next)
                    634:                        if ((ival = arg_width(nn)) >= 0)
                    635:                                len = (size_t)ival;
1.8       schwarze  636:
1.11      schwarze  637:        switch (n->type) {
                    638:        case (MAN_HEAD):
                    639:                /* Handle zero-length properly. */
                    640:                if (0 == len)
                    641:                        len = 1;
                    642:
1.13    ! schwarze  643:                p->offset = mt->offset;
        !           644:                p->rmargin = mt->offset + len;
1.11      schwarze  645:
                    646:                /* Don't print same-line elements. */
                    647:                for (nn = n->child; nn; nn = nn->next)
                    648:                        if (nn->line > n->line)
                    649:                                print_node(p, mt, nn, m);
                    650:
                    651:                if (ival >= 0)
1.13    ! schwarze  652:                        mt->lmargin = (size_t)ival;
1.11      schwarze  653:
                    654:                return(0);
                    655:        case (MAN_BODY):
1.13    ! schwarze  656:                p->offset = mt->offset + len;
1.11      schwarze  657:                p->rmargin = p->maxrmargin;
                    658:                break;
                    659:        default:
                    660:                break;
                    661:        }
1.1       kristaps  662:
1.11      schwarze  663:        return(1);
                    664: }
1.1       kristaps  665:
                    666:
1.11      schwarze  667: /* ARGSUSED */
                    668: static void
                    669: post_TP(DECL_ARGS)
                    670: {
1.1       kristaps  671:
1.11      schwarze  672:        switch (n->type) {
                    673:        case (MAN_HEAD):
                    674:                term_flushln(p);
                    675:                p->flags &= ~TERMP_NOBREAK;
                    676:                p->flags &= ~TERMP_TWOSPACE;
                    677:                p->rmargin = p->maxrmargin;
                    678:                break;
                    679:        case (MAN_BODY):
                    680:                term_flushln(p);
                    681:                p->flags &= ~TERMP_NOLPAD;
                    682:                break;
                    683:        default:
                    684:                break;
                    685:        }
1.1       kristaps  686: }
                    687:
                    688:
                    689: /* ARGSUSED */
                    690: static int
                    691: pre_SS(DECL_ARGS)
                    692: {
                    693:
1.11      schwarze  694:        switch (n->type) {
                    695:        case (MAN_BLOCK):
                    696:                mt->lmargin = INDENT;
1.13    ! schwarze  697:                mt->offset = INDENT;
1.11      schwarze  698:                /* If following a prior empty `SS', no vspace. */
                    699:                if (n->prev && MAN_SS == n->prev->tok)
                    700:                        if (NULL == n->prev->body->child)
                    701:                                break;
                    702:                if (NULL == n->prev)
                    703:                        break;
                    704:                term_vspace(p);
                    705:                break;
                    706:        case (MAN_HEAD):
                    707:                p->flags |= TERMP_BOLD;
                    708:                p->offset = HALFINDENT;
                    709:                break;
                    710:        case (MAN_BODY):
1.13    ! schwarze  711:                p->offset = mt->offset;
1.11      schwarze  712:                break;
                    713:        default:
                    714:                break;
                    715:        }
                    716:
1.1       kristaps  717:        return(1);
                    718: }
                    719:
                    720:
                    721: /* ARGSUSED */
                    722: static void
                    723: post_SS(DECL_ARGS)
                    724: {
                    725:
1.11      schwarze  726:        switch (n->type) {
                    727:        case (MAN_HEAD):
                    728:                term_newln(p);
                    729:                p->flags &= ~TERMP_BOLD;
                    730:                break;
                    731:        case (MAN_BODY):
                    732:                term_newln(p);
                    733:                break;
                    734:        default:
                    735:                break;
                    736:        }
1.1       kristaps  737: }
                    738:
                    739:
                    740: /* ARGSUSED */
                    741: static int
                    742: pre_SH(DECL_ARGS)
                    743: {
                    744:
1.11      schwarze  745:        switch (n->type) {
                    746:        case (MAN_BLOCK):
                    747:                mt->lmargin = INDENT;
1.13    ! schwarze  748:                mt->offset = INDENT;
1.11      schwarze  749:                /* If following a prior empty `SH', no vspace. */
                    750:                if (n->prev && MAN_SH == n->prev->tok)
                    751:                        if (NULL == n->prev->body->child)
                    752:                                break;
                    753:                term_vspace(p);
                    754:                break;
                    755:        case (MAN_HEAD):
                    756:                p->flags |= TERMP_BOLD;
                    757:                p->offset = 0;
                    758:                break;
                    759:        case (MAN_BODY):
1.13    ! schwarze  760:                p->offset = mt->offset;
1.11      schwarze  761:                break;
                    762:        default:
                    763:                break;
                    764:        }
                    765:
1.1       kristaps  766:        return(1);
                    767: }
                    768:
                    769:
                    770: /* ARGSUSED */
                    771: static void
                    772: post_SH(DECL_ARGS)
                    773: {
                    774:
1.11      schwarze  775:        switch (n->type) {
                    776:        case (MAN_HEAD):
                    777:                term_newln(p);
                    778:                p->flags &= ~TERMP_BOLD;
                    779:                break;
                    780:        case (MAN_BODY):
                    781:                term_newln(p);
                    782:                break;
                    783:        default:
1.13    ! schwarze  784:                break;
        !           785:        }
        !           786: }
        !           787:
        !           788:
        !           789: /* ARGSUSED */
        !           790: static int
        !           791: pre_RS(DECL_ARGS)
        !           792: {
        !           793:        const struct man_node   *nn;
        !           794:        int                      ival;
        !           795:
        !           796:        switch (n->type) {
        !           797:        case (MAN_BLOCK):
        !           798:                term_newln(p);
        !           799:                return(1);
        !           800:        case (MAN_HEAD):
        !           801:                return(0);
        !           802:        default:
        !           803:                break;
        !           804:        }
        !           805:
        !           806:        if (NULL == (nn = n->parent->head->child)) {
        !           807:                mt->offset = mt->lmargin + INDENT;
        !           808:                p->offset = mt->offset;
        !           809:                return(1);
        !           810:        }
        !           811:
        !           812:        if ((ival = arg_width(nn)) < 0)
        !           813:                return(1);
        !           814:
        !           815:        mt->offset = INDENT + (size_t)ival;
        !           816:        p->offset = mt->offset;
        !           817:
        !           818:        return(1);
        !           819: }
        !           820:
        !           821:
        !           822: /* ARGSUSED */
        !           823: static void
        !           824: post_RS(DECL_ARGS)
        !           825: {
        !           826:
        !           827:        switch (n->type) {
        !           828:        case (MAN_BLOCK):
        !           829:                mt->offset = mt->lmargin = INDENT;
        !           830:                break;
        !           831:        default:
        !           832:                term_newln(p);
        !           833:                p->offset = INDENT;
1.11      schwarze  834:                break;
                    835:        }
1.1       kristaps  836: }
                    837:
                    838:
                    839: static void
                    840: print_node(DECL_ARGS)
                    841: {
                    842:        int              c, sz;
                    843:
                    844:        c = 1;
                    845:
                    846:        switch (n->type) {
                    847:        case(MAN_TEXT):
                    848:                if (0 == *n->string) {
                    849:                        term_vspace(p);
                    850:                        break;
                    851:                }
                    852:                /*
                    853:                 * Note!  This is hacky.  Here, we recognise the `\c'
                    854:                 * escape embedded in so many -man pages.  It's supposed
                    855:                 * to remove the subsequent space, so we mark NOSPACE if
                    856:                 * it's encountered in the string.
                    857:                 */
                    858:                sz = (int)strlen(n->string);
                    859:                term_word(p, n->string);
                    860:                if (sz >= 2 && n->string[sz - 1] == 'c' &&
                    861:                                n->string[sz - 2] == '\\')
                    862:                        p->flags |= TERMP_NOSPACE;
1.11      schwarze  863:                /* FIXME: this means that macro lines are munged!  */
                    864:                if (MANT_LITERAL & mt->fl) {
                    865:                        p->flags |= TERMP_NOSPACE;
                    866:                        term_flushln(p);
                    867:                }
1.1       kristaps  868:                break;
                    869:        default:
1.11      schwarze  870:                if (termacts[n->tok].pre)
                    871:                        c = (*termacts[n->tok].pre)(p, mt, n, m);
1.1       kristaps  872:                break;
                    873:        }
                    874:
                    875:        if (c && n->child)
1.11      schwarze  876:                print_body(p, mt, n->child, m);
1.1       kristaps  877:
1.11      schwarze  878:        if (MAN_TEXT != n->type)
1.1       kristaps  879:                if (termacts[n->tok].post)
1.11      schwarze  880:                        (*termacts[n->tok].post)(p, mt, n, m);
1.1       kristaps  881: }
                    882:
                    883:
                    884: static void
                    885: print_body(DECL_ARGS)
                    886: {
1.11      schwarze  887:
                    888:        print_node(p, mt, n, m);
1.1       kristaps  889:        if ( ! n->next)
                    890:                return;
1.11      schwarze  891:        print_body(p, mt, n->next, m);
1.1       kristaps  892: }
                    893:
                    894:
                    895: static void
                    896: print_foot(struct termp *p, const struct man_meta *meta)
                    897: {
                    898:        struct tm       *tm;
                    899:        char            *buf;
                    900:
                    901:        if (NULL == (buf = malloc(p->rmargin)))
                    902:                err(1, "malloc");
                    903:
                    904:        tm = localtime(&meta->date);
                    905:
1.3       schwarze  906:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
1.1       kristaps  907:                err(1, "strftime");
                    908:
                    909:        term_vspace(p);
                    910:
                    911:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    912:        p->rmargin = p->maxrmargin - strlen(buf);
                    913:        p->offset = 0;
                    914:
                    915:        if (meta->source)
                    916:                term_word(p, meta->source);
                    917:        if (meta->source)
                    918:                term_word(p, "");
                    919:        term_flushln(p);
                    920:
                    921:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    922:        p->offset = p->rmargin;
                    923:        p->rmargin = p->maxrmargin;
                    924:        p->flags &= ~TERMP_NOBREAK;
                    925:
                    926:        term_word(p, buf);
                    927:        term_flushln(p);
                    928:
                    929:        free(buf);
                    930: }
                    931:
                    932:
                    933: static void
                    934: print_head(struct termp *p, const struct man_meta *meta)
                    935: {
                    936:        char            *buf, *title;
                    937:
                    938:        p->rmargin = p->maxrmargin;
                    939:        p->offset = 0;
                    940:
                    941:        if (NULL == (buf = malloc(p->rmargin)))
                    942:                err(1, "malloc");
                    943:        if (NULL == (title = malloc(p->rmargin)))
                    944:                err(1, "malloc");
                    945:
                    946:        if (meta->vol)
                    947:                (void)strlcpy(buf, meta->vol, p->rmargin);
                    948:        else
                    949:                *buf = 0;
                    950:
                    951:        (void)snprintf(title, p->rmargin, "%s(%d)",
                    952:                        meta->title, meta->msec);
                    953:
                    954:        p->offset = 0;
1.5       schwarze  955:        p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1       kristaps  956:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                    957:
                    958:        term_word(p, title);
                    959:        term_flushln(p);
                    960:
                    961:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    962:        p->offset = p->rmargin;
                    963:        p->rmargin = p->maxrmargin - strlen(title);
                    964:
                    965:        term_word(p, buf);
                    966:        term_flushln(p);
                    967:
                    968:        p->offset = p->rmargin;
                    969:        p->rmargin = p->maxrmargin;
                    970:        p->flags &= ~TERMP_NOBREAK;
                    971:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    972:
                    973:        term_word(p, title);
                    974:        term_flushln(p);
                    975:
                    976:        p->rmargin = p->maxrmargin;
                    977:        p->offset = 0;
                    978:        p->flags &= ~TERMP_NOSPACE;
                    979:
                    980:        free(title);
                    981:        free(buf);
                    982: }
                    983: