[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.14

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