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

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