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

Annotation of mandoc/man_term.c, Revision 1.21

1.21    ! kristaps    1: /*     $Id: man_term.c,v 1.20 2009/08/13 12:15:58 kristaps Exp $ */
1.1       kristaps    2: /*
1.9       kristaps    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.8       kristaps    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.8       kristaps    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:  */
                     17: #include <assert.h>
1.18      kristaps   18: #include <ctype.h>
1.1       kristaps   19: #include <err.h>
                     20: #include <stdio.h>
                     21: #include <stdlib.h>
                     22: #include <string.h>
                     23:
                     24: #include "term.h"
                     25: #include "man.h"
                     26:
1.18      kristaps   27: #define        INDENT            7
                     28: #define        HALFINDENT        3
                     29:
1.2       kristaps   30: #ifdef __linux__
                     31: extern size_t            strlcpy(char *, const char *, size_t);
                     32: extern size_t            strlcat(char *, const char *, size_t);
                     33: #endif
                     34:
1.19      kristaps   35: #define        MANT_LITERAL     (1 << 0)
                     36:
1.1       kristaps   37: #define        DECL_ARGS         struct termp *p, \
1.19      kristaps   38:                          int *fl, \
1.1       kristaps   39:                          const struct man_node *n, \
                     40:                          const struct man_meta *m
                     41:
                     42: struct termact {
                     43:        int             (*pre)(DECL_ARGS);
                     44:        void            (*post)(DECL_ARGS);
                     45: };
                     46:
                     47: static int               pre_B(DECL_ARGS);
1.3       kristaps   48: static int               pre_BI(DECL_ARGS);
                     49: static int               pre_BR(DECL_ARGS);
1.19      kristaps   50: static int               pre_HP(DECL_ARGS);
1.1       kristaps   51: static int               pre_I(DECL_ARGS);
1.3       kristaps   52: static int               pre_IB(DECL_ARGS);
1.4       kristaps   53: static int               pre_IP(DECL_ARGS);
1.3       kristaps   54: static int               pre_IR(DECL_ARGS);
1.1       kristaps   55: static int               pre_PP(DECL_ARGS);
1.3       kristaps   56: static int               pre_RB(DECL_ARGS);
                     57: static int               pre_RI(DECL_ARGS);
1.1       kristaps   58: static int               pre_SH(DECL_ARGS);
                     59: static int               pre_SS(DECL_ARGS);
                     60: static int               pre_TP(DECL_ARGS);
1.19      kristaps   61: static int               pre_br(DECL_ARGS);
                     62: static int               pre_fi(DECL_ARGS);
                     63: static int               pre_nf(DECL_ARGS);
                     64: static int               pre_r(DECL_ARGS);
                     65: static int               pre_sp(DECL_ARGS);
1.1       kristaps   66:
                     67: static void              post_B(DECL_ARGS);
                     68: static void              post_I(DECL_ARGS);
1.20      kristaps   69: static void              post_HP(DECL_ARGS);
1.1       kristaps   70: static void              post_SH(DECL_ARGS);
                     71: static void              post_SS(DECL_ARGS);
1.21    ! kristaps   72: static void              post_TP(DECL_ARGS);
1.19      kristaps   73: static void              post_i(DECL_ARGS);
1.1       kristaps   74:
                     75: static const struct termact termacts[MAN_MAX] = {
1.15      kristaps   76:        { pre_br, NULL }, /* br */
1.1       kristaps   77:        { NULL, NULL }, /* TH */
                     78:        { pre_SH, post_SH }, /* SH */
                     79:        { pre_SS, post_SS }, /* SS */
1.21    ! kristaps   80:        { pre_TP, post_TP }, /* TP */
1.1       kristaps   81:        { pre_PP, NULL }, /* LP */
                     82:        { pre_PP, NULL }, /* PP */
                     83:        { pre_PP, NULL }, /* P */
1.4       kristaps   84:        { pre_IP, NULL }, /* IP */
1.20      kristaps   85:        { pre_HP, post_HP }, /* HP */
1.1       kristaps   86:        { NULL, NULL }, /* SM */
                     87:        { pre_B, post_B }, /* SB */
1.3       kristaps   88:        { pre_BI, NULL }, /* BI */
                     89:        { pre_IB, NULL }, /* IB */
                     90:        { pre_BR, NULL }, /* BR */
                     91:        { pre_RB, NULL }, /* RB */
1.1       kristaps   92:        { NULL, NULL }, /* R */
                     93:        { pre_B, post_B }, /* B */
                     94:        { pre_I, post_I }, /* I */
1.3       kristaps   95:        { pre_IR, NULL }, /* IR */
                     96:        { pre_RI, NULL }, /* RI */
1.19      kristaps   97:        { NULL, NULL }, /* na */ /* TODO: document that has no effect */
                     98:        { pre_I, post_i }, /* i */
                     99:        { pre_sp, NULL }, /* sp */
                    100:        { pre_nf, NULL }, /* nf */
                    101:        { pre_fi, NULL }, /* fi */
                    102:        { pre_r, NULL }, /* r */
1.1       kristaps  103: };
                    104:
                    105: static void              print_head(struct termp *,
                    106:                                const struct man_meta *);
                    107: static void              print_body(DECL_ARGS);
                    108: static void              print_node(DECL_ARGS);
                    109: static void              print_foot(struct termp *,
                    110:                                const struct man_meta *);
1.18      kristaps  111: static void              fmt_block_vspace(struct termp *,
                    112:                                const struct man_node *);
                    113: static int               arg_width(const struct man_node *);
1.1       kristaps  114:
                    115:
                    116: int
                    117: man_run(struct termp *p, const struct man *m)
                    118: {
1.19      kristaps  119:        int              fl;
1.1       kristaps  120:
                    121:        print_head(p, man_meta(m));
                    122:        p->flags |= TERMP_NOSPACE;
1.14      kristaps  123:        assert(man_node(m));
                    124:        assert(MAN_ROOT == man_node(m)->type);
1.19      kristaps  125:
                    126:        fl = 0;
1.14      kristaps  127:        if (man_node(m)->child)
1.19      kristaps  128:                print_body(p, &fl, man_node(m)->child, man_meta(m));
1.1       kristaps  129:        print_foot(p, man_meta(m));
                    130:
                    131:        return(1);
                    132: }
                    133:
                    134:
1.18      kristaps  135: static void
                    136: fmt_block_vspace(struct termp *p, const struct man_node *n)
                    137: {
                    138:        term_newln(p);
                    139:
                    140:        if (NULL == n->prev)
                    141:                return;
                    142:
                    143:        if (MAN_SS == n->prev->tok)
                    144:                return;
                    145:        if (MAN_SH == n->prev->tok)
                    146:                return;
                    147:
                    148:        term_vspace(p);
                    149: }
                    150:
                    151:
                    152: static int
                    153: arg_width(const struct man_node *n)
                    154: {
                    155:        int              i, len;
                    156:        const char      *p;
                    157:
                    158:        assert(MAN_TEXT == n->type);
                    159:        assert(n->string);
                    160:
                    161:        p = n->string;
                    162:
                    163:        if (0 == (len = (int)strlen(p)))
                    164:                return(-1);
                    165:
                    166:        for (i = 0; i < len; i++)
                    167:                if ( ! isdigit((u_char)p[i]))
                    168:                        break;
                    169:
                    170:        if (i == len - 1)  {
                    171:                if ('n' == p[len - 1] || 'm' == p[len - 1])
                    172:                        return(atoi(p));
                    173:        } else if (i == len)
                    174:                return(atoi(p));
                    175:
                    176:        return(-1);
                    177: }
                    178:
                    179:
1.3       kristaps  180: /* ARGSUSED */
1.1       kristaps  181: static int
                    182: pre_I(DECL_ARGS)
                    183: {
                    184:
                    185:        p->flags |= TERMP_UNDER;
                    186:        return(1);
                    187: }
                    188:
                    189:
1.3       kristaps  190: /* ARGSUSED */
1.19      kristaps  191: static int
                    192: pre_r(DECL_ARGS)
                    193: {
                    194:
                    195:        p->flags &= ~TERMP_UNDER;
                    196:        p->flags &= ~TERMP_BOLD;
                    197:        return(1);
                    198: }
                    199:
                    200:
                    201: /* ARGSUSED */
                    202: static void
                    203: post_i(DECL_ARGS)
                    204: {
                    205:
                    206:        if (n->nchild)
                    207:                p->flags &= ~TERMP_UNDER;
                    208: }
                    209:
                    210:
                    211: /* ARGSUSED */
1.1       kristaps  212: static void
                    213: post_I(DECL_ARGS)
                    214: {
                    215:
                    216:        p->flags &= ~TERMP_UNDER;
                    217: }
                    218:
                    219:
1.3       kristaps  220: /* ARGSUSED */
                    221: static int
1.19      kristaps  222: pre_fi(DECL_ARGS)
                    223: {
                    224:
                    225:        *fl &= ~MANT_LITERAL;
                    226:        return(1);
                    227: }
                    228:
                    229:
                    230: /* ARGSUSED */
                    231: static int
                    232: pre_nf(DECL_ARGS)
                    233: {
                    234:
                    235:        term_newln(p);
                    236:        *fl |= MANT_LITERAL;
                    237:        return(1);
                    238: }
                    239:
                    240:
                    241: /* ARGSUSED */
                    242: static int
1.3       kristaps  243: pre_IR(DECL_ARGS)
                    244: {
                    245:        const struct man_node *nn;
                    246:        int              i;
                    247:
                    248:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    249:                if ( ! (i % 2))
                    250:                        p->flags |= TERMP_UNDER;
1.4       kristaps  251:                if (i > 0)
                    252:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  253:                print_node(p, fl, nn, m);
1.3       kristaps  254:                if ( ! (i % 2))
                    255:                        p->flags &= ~TERMP_UNDER;
                    256:        }
                    257:        return(0);
                    258: }
                    259:
                    260:
                    261: /* ARGSUSED */
                    262: static int
                    263: pre_IB(DECL_ARGS)
                    264: {
                    265:        const struct man_node *nn;
                    266:        int              i;
                    267:
                    268:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    269:                p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
1.4       kristaps  270:                if (i > 0)
                    271:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  272:                print_node(p, fl, nn, m);
1.3       kristaps  273:                p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
                    274:        }
                    275:        return(0);
                    276: }
                    277:
                    278:
                    279: /* ARGSUSED */
                    280: static int
                    281: pre_RB(DECL_ARGS)
                    282: {
                    283:        const struct man_node *nn;
                    284:        int              i;
                    285:
                    286:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    287:                if (i % 2)
                    288:                        p->flags |= TERMP_BOLD;
1.4       kristaps  289:                if (i > 0)
                    290:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  291:                print_node(p, fl, nn, m);
1.3       kristaps  292:                if (i % 2)
                    293:                        p->flags &= ~TERMP_BOLD;
                    294:        }
                    295:        return(0);
                    296: }
                    297:
                    298:
                    299: /* ARGSUSED */
                    300: static int
                    301: pre_RI(DECL_ARGS)
                    302: {
                    303:        const struct man_node *nn;
                    304:        int              i;
                    305:
                    306:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    307:                if ( ! (i % 2))
                    308:                        p->flags |= TERMP_UNDER;
1.4       kristaps  309:                if (i > 0)
                    310:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  311:                print_node(p, fl, nn, m);
1.3       kristaps  312:                if ( ! (i % 2))
                    313:                        p->flags &= ~TERMP_UNDER;
                    314:        }
                    315:        return(0);
                    316: }
                    317:
                    318:
                    319: /* ARGSUSED */
                    320: static int
                    321: pre_BR(DECL_ARGS)
                    322: {
                    323:        const struct man_node *nn;
                    324:        int              i;
                    325:
                    326:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    327:                if ( ! (i % 2))
                    328:                        p->flags |= TERMP_BOLD;
1.4       kristaps  329:                if (i > 0)
                    330:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  331:                print_node(p, fl, nn, m);
1.3       kristaps  332:                if ( ! (i % 2))
                    333:                        p->flags &= ~TERMP_BOLD;
                    334:        }
                    335:        return(0);
                    336: }
                    337:
                    338:
                    339: /* ARGSUSED */
                    340: static int
                    341: pre_BI(DECL_ARGS)
                    342: {
                    343:        const struct man_node *nn;
                    344:        int              i;
                    345:
                    346:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    347:                p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
1.4       kristaps  348:                if (i > 0)
                    349:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  350:                print_node(p, fl, nn, m);
1.3       kristaps  351:                p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
                    352:        }
                    353:        return(0);
                    354: }
                    355:
                    356:
                    357: /* ARGSUSED */
1.1       kristaps  358: static int
                    359: pre_B(DECL_ARGS)
                    360: {
                    361:
                    362:        p->flags |= TERMP_BOLD;
                    363:        return(1);
                    364: }
                    365:
                    366:
1.3       kristaps  367: /* ARGSUSED */
1.1       kristaps  368: static void
                    369: post_B(DECL_ARGS)
                    370: {
                    371:
                    372:        p->flags &= ~TERMP_BOLD;
                    373: }
                    374:
                    375:
1.3       kristaps  376: /* ARGSUSED */
1.1       kristaps  377: static int
1.19      kristaps  378: pre_sp(DECL_ARGS)
                    379: {
                    380:        int              i, len;
                    381:
                    382:        if (NULL == n->child) {
                    383:                term_vspace(p);
                    384:                return(0);
                    385:        }
                    386:
                    387:        len = atoi(n->child->string);
                    388:        if (0 == len)
                    389:                term_newln(p);
                    390:        for (i = 0; i < len; i++)
                    391:                term_vspace(p);
                    392:
                    393:        return(0);
                    394: }
                    395:
                    396:
                    397: /* ARGSUSED */
                    398: static int
1.15      kristaps  399: pre_br(DECL_ARGS)
                    400: {
                    401:
                    402:        term_newln(p);
                    403:        return(0);
                    404: }
                    405:
                    406:
                    407: /* ARGSUSED */
                    408: static int
1.19      kristaps  409: pre_HP(DECL_ARGS)
                    410: {
                    411:
1.20      kristaps  412:        switch (n->type) {
                    413:        case (MAN_BLOCK):
                    414:                fmt_block_vspace(p, n);
                    415:                break;
                    416:        case (MAN_BODY):
                    417:                p->flags |= TERMP_NOBREAK;
                    418:                p->flags |= TERMP_TWOSPACE;
                    419:                p->offset = INDENT;
                    420:                p->rmargin = INDENT * 2;
                    421:                break;
                    422:        default:
                    423:                return(0);
                    424:        }
                    425:
1.19      kristaps  426:        return(1);
                    427: }
                    428:
                    429:
                    430: /* ARGSUSED */
1.20      kristaps  431: static void
                    432: post_HP(DECL_ARGS)
                    433: {
                    434:
                    435:        switch (n->type) {
                    436:        case (MAN_BODY):
                    437:                term_flushln(p);
                    438:                p->flags &= ~TERMP_NOBREAK;
                    439:                p->flags &= ~TERMP_TWOSPACE;
                    440:                p->offset = INDENT;
                    441:                p->rmargin = p->maxrmargin;
                    442:                break;
                    443:        default:
                    444:                break;
                    445:        }
                    446: }
                    447:
                    448:
                    449: /* ARGSUSED */
1.19      kristaps  450: static int
1.1       kristaps  451: pre_PP(DECL_ARGS)
                    452: {
                    453:
1.19      kristaps  454:        switch (n->type) {
                    455:        case (MAN_BLOCK):
                    456:                fmt_block_vspace(p, n);
                    457:                break;
                    458:        default:
                    459:                p->offset = INDENT;
                    460:                break;
                    461:        }
                    462:
                    463:        return(1);
1.1       kristaps  464: }
                    465:
                    466:
1.3       kristaps  467: /* ARGSUSED */
1.1       kristaps  468: static int
1.4       kristaps  469: pre_IP(DECL_ARGS)
                    470: {
1.19      kristaps  471:        /* TODO */
                    472: #if 0
1.4       kristaps  473:        const struct man_node *nn;
1.18      kristaps  474:        size_t           offs, sv;
                    475:        int              ival;
                    476:
                    477:        fmt_block_vspace(p, n);
                    478:
                    479:        p->flags |= TERMP_NOSPACE;
1.4       kristaps  480:
1.18      kristaps  481:        sv = p->offset;
1.4       kristaps  482:        p->offset = INDENT;
                    483:
1.18      kristaps  484:        if (NULL == n->child)
1.4       kristaps  485:                return(1);
                    486:
1.18      kristaps  487:        p->flags |= TERMP_NOBREAK;
                    488:
                    489:        offs = sv;
                    490:
                    491:        /*
                    492:         * If the last token is number-looking (3m, 3n, 3) then
                    493:         * interpret it as the width specifier, else we stick with the
                    494:         * prior saved offset.  XXX - obviously not documented.
                    495:         */
                    496:        for (nn = n->child; nn; nn = nn->next) {
                    497:                if (NULL == nn->next) {
                    498:                        ival = arg_width(nn);
                    499:                        if (ival >= 0) {
                    500:                                offs = (size_t)ival;
                    501:                                break;
                    502:                        }
                    503:                }
1.19      kristaps  504:                print_node(p, fl, nn, m);
1.18      kristaps  505:        }
                    506:
                    507:        p->rmargin = p->offset + offs;
                    508:
                    509:        term_flushln(p);
                    510:
                    511:        p->offset = offs;
                    512:        p->rmargin = p->maxrmargin;
1.4       kristaps  513:
1.18      kristaps  514:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.12      kristaps  515:
1.4       kristaps  516:        return(0);
1.19      kristaps  517: #endif
                    518:        return(1);
1.4       kristaps  519: }
                    520:
                    521:
                    522: /* ARGSUSED */
                    523: static int
1.1       kristaps  524: pre_TP(DECL_ARGS)
                    525: {
                    526:
1.21    ! kristaps  527:        switch (n->type) {
        !           528:        case (MAN_BLOCK):
        !           529:                fmt_block_vspace(p, n);
        !           530:                break;
        !           531:        case (MAN_HEAD):
        !           532:                p->rmargin = INDENT * 2;
        !           533:                p->offset = INDENT;
        !           534:                p->flags |= TERMP_NOBREAK;
        !           535:                p->flags |= TERMP_TWOSPACE;
        !           536:                break;
        !           537:        case (MAN_BODY):
        !           538:                p->flags |= TERMP_NOLPAD;
        !           539:                p->flags |= TERMP_NOSPACE;
        !           540:                p->offset = INDENT * 2;
        !           541:                break;
        !           542:        default:
        !           543:                break;
        !           544:        }
1.16      kristaps  545:
1.21    ! kristaps  546:        return(1);
        !           547: }
1.1       kristaps  548:
                    549:
1.21    ! kristaps  550: /* ARGSUSED */
        !           551: static void
        !           552: post_TP(DECL_ARGS)
        !           553: {
1.1       kristaps  554:
1.21    ! kristaps  555:        switch (n->type) {
        !           556:        case (MAN_HEAD):
        !           557:                term_flushln(p);
        !           558:                p->flags &= ~TERMP_NOBREAK;
        !           559:                p->flags &= ~TERMP_TWOSPACE;
        !           560:                p->rmargin = p->maxrmargin;
        !           561:                break;
        !           562:        case (MAN_BODY):
        !           563:                term_flushln(p);
        !           564:                p->flags &= ~TERMP_NOLPAD;
        !           565:                break;
        !           566:        default:
        !           567:                break;
        !           568:        }
1.1       kristaps  569: }
                    570:
                    571:
1.3       kristaps  572: /* ARGSUSED */
1.1       kristaps  573: static int
                    574: pre_SS(DECL_ARGS)
                    575: {
                    576:
1.19      kristaps  577:        switch (n->type) {
                    578:        case (MAN_BLOCK):
                    579:                term_newln(p);
                    580:                if (n->prev)
                    581:                        term_vspace(p);
                    582:                break;
                    583:        case (MAN_HEAD):
                    584:                p->flags |= TERMP_BOLD;
                    585:                p->offset = HALFINDENT;
                    586:                break;
                    587:        default:
                    588:                p->offset = INDENT;
                    589:                break;
                    590:        }
                    591:
1.1       kristaps  592:        return(1);
                    593: }
                    594:
                    595:
1.3       kristaps  596: /* ARGSUSED */
1.1       kristaps  597: static void
                    598: post_SS(DECL_ARGS)
                    599: {
                    600:
1.19      kristaps  601:        switch (n->type) {
                    602:        case (MAN_HEAD):
                    603:                term_newln(p);
                    604:                p->flags &= ~TERMP_BOLD;
                    605:                break;
                    606:        default:
                    607:                break;
                    608:        }
1.1       kristaps  609: }
                    610:
                    611:
1.3       kristaps  612: /* ARGSUSED */
1.1       kristaps  613: static int
                    614: pre_SH(DECL_ARGS)
                    615: {
1.19      kristaps  616:        /*
                    617:         * XXX: undocumented: using two `SH' macros in sequence has no
                    618:         * vspace between calls, only a newline.
                    619:         */
                    620:        switch (n->type) {
                    621:        case (MAN_BLOCK):
                    622:                if (n->prev && MAN_SH == n->prev->tok)
                    623:                        if (NULL == n->prev->body->child)
                    624:                                break;
                    625:                term_vspace(p);
                    626:                break;
                    627:        case (MAN_HEAD):
                    628:                p->flags |= TERMP_BOLD;
                    629:                p->offset = 0;
                    630:                break;
                    631:        case (MAN_BODY):
                    632:                p->offset = INDENT;
                    633:                break;
                    634:        default:
                    635:                break;
                    636:        }
1.1       kristaps  637:
                    638:        return(1);
                    639: }
                    640:
                    641:
1.3       kristaps  642: /* ARGSUSED */
1.1       kristaps  643: static void
                    644: post_SH(DECL_ARGS)
                    645: {
                    646:
1.19      kristaps  647:        switch (n->type) {
                    648:        case (MAN_HEAD):
                    649:                term_newln(p);
                    650:                p->flags &= ~TERMP_BOLD;
                    651:                break;
                    652:        case (MAN_BODY):
                    653:                term_newln(p);
                    654:                break;
                    655:        default:
                    656:                break;
                    657:        }
1.1       kristaps  658: }
                    659:
                    660:
                    661: static void
                    662: print_node(DECL_ARGS)
                    663: {
1.4       kristaps  664:        int              c, sz;
1.1       kristaps  665:
                    666:        c = 1;
                    667:
                    668:        switch (n->type) {
                    669:        case(MAN_TEXT):
1.4       kristaps  670:                if (0 == *n->string) {
                    671:                        term_vspace(p);
1.1       kristaps  672:                        break;
                    673:                }
1.4       kristaps  674:                /*
                    675:                 * Note!  This is hacky.  Here, we recognise the `\c'
                    676:                 * escape embedded in so many -man pages.  It's supposed
                    677:                 * to remove the subsequent space, so we mark NOSPACE if
                    678:                 * it's encountered in the string.
                    679:                 */
                    680:                sz = (int)strlen(n->string);
                    681:                term_word(p, n->string);
                    682:                if (sz >= 2 && n->string[sz - 1] == 'c' &&
                    683:                                n->string[sz - 2] == '\\')
                    684:                        p->flags |= TERMP_NOSPACE;
1.19      kristaps  685:                /* FIXME: this means that macro lines are munged!  */
                    686:                if (MANT_LITERAL & *fl) {
                    687:                        p->flags |= TERMP_NOSPACE;
                    688:                        term_flushln(p);
                    689:                }
1.1       kristaps  690:                break;
                    691:        default:
1.19      kristaps  692:                if (termacts[n->tok].pre)
                    693:                        c = (*termacts[n->tok].pre)(p, fl, n, m);
1.1       kristaps  694:                break;
                    695:        }
                    696:
                    697:        if (c && n->child)
1.19      kristaps  698:                print_body(p, fl, n->child, m);
1.1       kristaps  699:
1.19      kristaps  700:        if (MAN_TEXT != n->type)
1.1       kristaps  701:                if (termacts[n->tok].post)
1.19      kristaps  702:                        (*termacts[n->tok].post)(p, fl, n, m);
1.1       kristaps  703: }
                    704:
                    705:
                    706: static void
                    707: print_body(DECL_ARGS)
                    708: {
1.19      kristaps  709:
                    710:        print_node(p, fl, n, m);
1.1       kristaps  711:        if ( ! n->next)
                    712:                return;
1.19      kristaps  713:        print_body(p, fl, n->next, m);
1.1       kristaps  714: }
                    715:
                    716:
                    717: static void
                    718: print_foot(struct termp *p, const struct man_meta *meta)
                    719: {
                    720:        struct tm       *tm;
                    721:        char            *buf;
                    722:
                    723:        if (NULL == (buf = malloc(p->rmargin)))
                    724:                err(1, "malloc");
                    725:
                    726:        tm = localtime(&meta->date);
                    727:
                    728:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    729:                err(1, "strftime");
                    730:
                    731:        term_vspace(p);
                    732:
                    733:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    734:        p->rmargin = p->maxrmargin - strlen(buf);
                    735:        p->offset = 0;
                    736:
                    737:        if (meta->source)
                    738:                term_word(p, meta->source);
                    739:        if (meta->source)
                    740:                term_word(p, "");
                    741:        term_flushln(p);
                    742:
                    743:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    744:        p->offset = p->rmargin;
                    745:        p->rmargin = p->maxrmargin;
                    746:        p->flags &= ~TERMP_NOBREAK;
                    747:
                    748:        term_word(p, buf);
                    749:        term_flushln(p);
                    750:
                    751:        free(buf);
                    752: }
                    753:
                    754:
                    755: static void
                    756: print_head(struct termp *p, const struct man_meta *meta)
                    757: {
                    758:        char            *buf, *title;
                    759:
                    760:        p->rmargin = p->maxrmargin;
                    761:        p->offset = 0;
                    762:
                    763:        if (NULL == (buf = malloc(p->rmargin)))
                    764:                err(1, "malloc");
                    765:        if (NULL == (title = malloc(p->rmargin)))
                    766:                err(1, "malloc");
                    767:
                    768:        if (meta->vol)
                    769:                (void)strlcpy(buf, meta->vol, p->rmargin);
                    770:        else
                    771:                *buf = 0;
                    772:
                    773:        (void)snprintf(title, p->rmargin, "%s(%d)",
                    774:                        meta->title, meta->msec);
                    775:
                    776:        p->offset = 0;
1.10      kristaps  777:        p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1       kristaps  778:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                    779:
                    780:        term_word(p, title);
                    781:        term_flushln(p);
                    782:
                    783:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    784:        p->offset = p->rmargin;
                    785:        p->rmargin = p->maxrmargin - strlen(title);
                    786:
                    787:        term_word(p, buf);
                    788:        term_flushln(p);
                    789:
                    790:        p->offset = p->rmargin;
                    791:        p->rmargin = p->maxrmargin;
                    792:        p->flags &= ~TERMP_NOBREAK;
                    793:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    794:
                    795:        term_word(p, title);
                    796:        term_flushln(p);
                    797:
                    798:        p->rmargin = p->maxrmargin;
                    799:        p->offset = 0;
                    800:        p->flags &= ~TERMP_NOSPACE;
                    801:
                    802:        free(title);
                    803:        free(buf);
                    804: }
                    805:

CVSweb