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

1.66    ! schwarze    1: /*     $Id: man_term.c,v 1.65 2011/03/07 01:35:33 schwarze Exp $ */
1.1       kristaps    2: /*
1.58      schwarze    3:  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.57      schwarze    4:  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1       kristaps    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
1.2       schwarze    7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    9:  *
1.2       schwarze   10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       kristaps   17:  */
1.13      schwarze   18: #include <sys/types.h>
                     19:
1.1       kristaps   20: #include <assert.h>
1.11      schwarze   21: #include <ctype.h>
1.1       kristaps   22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
1.37      schwarze   26: #include "mandoc.h"
1.18      schwarze   27: #include "out.h"
                     28: #include "man.h"
1.1       kristaps   29: #include "term.h"
1.18      schwarze   30: #include "chars.h"
                     31: #include "main.h"
1.10      schwarze   32:
                     33: #define        INDENT            7
                     34: #define        HALFINDENT        3
1.1       kristaps   35:
1.19      schwarze   36: /* FIXME: have PD set the default vspace width. */
                     37:
1.11      schwarze   38: struct mtermp {
                     39:        int               fl;
                     40: #define        MANT_LITERAL     (1 << 0)
1.13      schwarze   41:        /*
                     42:         * Default amount to indent the left margin after leading text
                     43:         * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
                     44:         * indent).  This needs to be saved because `HP' and so on, if
                     45:         * not having a specified value, must default.
                     46:         *
                     47:         * Note that this is the indentation AFTER the left offset, so
                     48:         * the total offset is usually offset + lmargin.
                     49:         */
                     50:        size_t            lmargin;
                     51:        /*
                     52:         * The default offset, i.e., the amount between any text and the
                     53:         * page boundary.
                     54:         */
                     55:        size_t            offset;
1.11      schwarze   56: };
                     57:
1.1       kristaps   58: #define        DECL_ARGS         struct termp *p, \
1.11      schwarze   59:                          struct mtermp *mt, \
1.1       kristaps   60:                          const struct man_node *n, \
                     61:                          const struct man_meta *m
                     62:
                     63: struct termact {
                     64:        int             (*pre)(DECL_ARGS);
                     65:        void            (*post)(DECL_ARGS);
1.26      schwarze   66:        int               flags;
                     67: #define        MAN_NOTEXT       (1 << 0) /* Never has text children. */
1.1       kristaps   68: };
                     69:
1.42      schwarze   70: static int               a2width(const struct termp *, const char *);
                     71: static size_t            a2height(const struct termp *, const char *);
1.18      schwarze   72:
1.21      schwarze   73: static void              print_man_nodelist(DECL_ARGS);
1.19      schwarze   74: static void              print_man_node(DECL_ARGS);
1.41      schwarze   75: static void              print_man_head(struct termp *, const void *);
                     76: static void              print_man_foot(struct termp *, const void *);
1.18      schwarze   77: static void              print_bvspace(struct termp *,
                     78:                                const struct man_node *);
                     79:
1.51      schwarze   80: static int               pre_alternate(DECL_ARGS);
1.1       kristaps   81: static int               pre_B(DECL_ARGS);
1.11      schwarze   82: static int               pre_HP(DECL_ARGS);
1.1       kristaps   83: static int               pre_I(DECL_ARGS);
                     84: static int               pre_IP(DECL_ARGS);
                     85: static int               pre_PP(DECL_ARGS);
1.13      schwarze   86: static int               pre_RS(DECL_ARGS);
1.1       kristaps   87: static int               pre_SH(DECL_ARGS);
                     88: static int               pre_SS(DECL_ARGS);
                     89: static int               pre_TP(DECL_ARGS);
1.14      schwarze   90: static int               pre_ign(DECL_ARGS);
1.45      schwarze   91: static int               pre_in(DECL_ARGS);
                     92: static int               pre_literal(DECL_ARGS);
1.11      schwarze   93: static int               pre_sp(DECL_ARGS);
1.52      schwarze   94: static int               pre_ft(DECL_ARGS);
1.1       kristaps   95:
1.11      schwarze   96: static void              post_IP(DECL_ARGS);
                     97: static void              post_HP(DECL_ARGS);
1.13      schwarze   98: static void              post_RS(DECL_ARGS);
1.1       kristaps   99: static void              post_SH(DECL_ARGS);
                    100: static void              post_SS(DECL_ARGS);
1.11      schwarze  101: static void              post_TP(DECL_ARGS);
1.1       kristaps  102:
1.17      schwarze  103: static const struct termact termacts[MAN_MAX] = {
1.45      schwarze  104:        { pre_sp, NULL, MAN_NOTEXT }, /* br */
1.26      schwarze  105:        { NULL, NULL, 0 }, /* TH */
                    106:        { pre_SH, post_SH, 0 }, /* SH */
                    107:        { pre_SS, post_SS, 0 }, /* SS */
                    108:        { pre_TP, post_TP, 0 }, /* TP */
                    109:        { pre_PP, NULL, 0 }, /* LP */
                    110:        { pre_PP, NULL, 0 }, /* PP */
                    111:        { pre_PP, NULL, 0 }, /* P */
                    112:        { pre_IP, post_IP, 0 }, /* IP */
                    113:        { pre_HP, post_HP, 0 }, /* HP */
                    114:        { NULL, NULL, 0 }, /* SM */
                    115:        { pre_B, NULL, 0 }, /* SB */
1.51      schwarze  116:        { pre_alternate, NULL, 0 }, /* BI */
                    117:        { pre_alternate, NULL, 0 }, /* IB */
                    118:        { pre_alternate, NULL, 0 }, /* BR */
                    119:        { pre_alternate, NULL, 0 }, /* RB */
1.26      schwarze  120:        { NULL, NULL, 0 }, /* R */
                    121:        { pre_B, NULL, 0 }, /* B */
                    122:        { pre_I, NULL, 0 }, /* I */
1.51      schwarze  123:        { pre_alternate, NULL, 0 }, /* IR */
                    124:        { pre_alternate, NULL, 0 }, /* RI */
1.62      schwarze  125:        { pre_ign, NULL, MAN_NOTEXT }, /* na */
1.26      schwarze  126:        { pre_sp, NULL, MAN_NOTEXT }, /* sp */
1.45      schwarze  127:        { pre_literal, NULL, 0 }, /* nf */
                    128:        { pre_literal, NULL, 0 }, /* fi */
1.26      schwarze  129:        { NULL, NULL, 0 }, /* RE */
                    130:        { pre_RS, post_RS, 0 }, /* RS */
                    131:        { pre_ign, NULL, 0 }, /* DT */
                    132:        { pre_ign, NULL, 0 }, /* UC */
                    133:        { pre_ign, NULL, 0 }, /* PD */
1.36      schwarze  134:        { pre_ign, NULL, 0 }, /* AT */
1.45      schwarze  135:        { pre_in, NULL, MAN_NOTEXT }, /* in */
1.52      schwarze  136:        { pre_ft, NULL, MAN_NOTEXT }, /* ft */
1.1       kristaps  137: };
1.11      schwarze  138:
1.1       kristaps  139:
                    140:
1.16      schwarze  141: void
1.18      schwarze  142: terminal_man(void *arg, const struct man *man)
1.1       kristaps  143: {
1.18      schwarze  144:        struct termp            *p;
                    145:        const struct man_node   *n;
                    146:        const struct man_meta   *m;
                    147:        struct mtermp            mt;
                    148:
                    149:        p = (struct termp *)arg;
                    150:
1.39      schwarze  151:        p->overstep = 0;
1.32      schwarze  152:        p->maxrmargin = p->defrmargin;
1.42      schwarze  153:        p->tabwidth = term_len(p, 5);
1.27      schwarze  154:
1.18      schwarze  155:        if (NULL == p->symtab)
                    156:                switch (p->enc) {
                    157:                case (TERMENC_ASCII):
                    158:                        p->symtab = chars_init(CHARS_ASCII);
                    159:                        break;
                    160:                default:
                    161:                        abort();
                    162:                        /* NOTREACHED */
                    163:                }
                    164:
                    165:        n = man_node(man);
                    166:        m = man_meta(man);
1.1       kristaps  167:
1.41      schwarze  168:        term_begin(p, print_man_head, print_man_foot, m);
1.1       kristaps  169:        p->flags |= TERMP_NOSPACE;
1.11      schwarze  170:
                    171:        mt.fl = 0;
1.42      schwarze  172:        mt.lmargin = term_len(p, INDENT);
                    173:        mt.offset = term_len(p, INDENT);
1.11      schwarze  174:
1.18      schwarze  175:        if (n->child)
1.21      schwarze  176:                print_man_nodelist(p, &mt, n->child, m);
1.41      schwarze  177:
                    178:        term_end(p);
1.1       kristaps  179: }
                    180:
                    181:
1.42      schwarze  182: static size_t
                    183: a2height(const struct termp *p, const char *cp)
1.11      schwarze  184: {
1.18      schwarze  185:        struct roffsu    su;
1.11      schwarze  186:
1.42      schwarze  187:        if ( ! a2roffsu(cp, &su, SCALE_VS))
                    188:                SCALE_VS_INIT(&su, term_strlen(p, cp));
1.11      schwarze  189:
1.42      schwarze  190:        return(term_vspan(p, &su));
1.11      schwarze  191: }
                    192:
                    193:
                    194: static int
1.42      schwarze  195: a2width(const struct termp *p, const char *cp)
1.11      schwarze  196: {
1.18      schwarze  197:        struct roffsu    su;
1.11      schwarze  198:
1.42      schwarze  199:        if ( ! a2roffsu(cp, &su, SCALE_BU))
1.18      schwarze  200:                return(-1);
                    201:
1.42      schwarze  202:        return((int)term_hspan(p, &su));
1.18      schwarze  203: }
1.11      schwarze  204:
                    205:
1.18      schwarze  206: static void
                    207: print_bvspace(struct termp *p, const struct man_node *n)
                    208: {
                    209:        term_newln(p);
1.64      schwarze  210:
                    211:        if (n->body && n->body->child && MAN_TBL == n->body->child->type)
                    212:                return;
1.11      schwarze  213:
1.18      schwarze  214:        if (NULL == n->prev)
                    215:                return;
1.11      schwarze  216:
1.18      schwarze  217:        if (MAN_SS == n->prev->tok)
                    218:                return;
                    219:        if (MAN_SH == n->prev->tok)
                    220:                return;
1.11      schwarze  221:
1.18      schwarze  222:        term_vspace(p);
1.14      schwarze  223: }
                    224:
                    225:
                    226: /* ARGSUSED */
                    227: static int
                    228: pre_ign(DECL_ARGS)
                    229: {
                    230:
                    231:        return(0);
1.11      schwarze  232: }
                    233:
                    234:
1.1       kristaps  235: /* ARGSUSED */
                    236: static int
                    237: pre_I(DECL_ARGS)
                    238: {
                    239:
1.21      schwarze  240:        term_fontrepl(p, TERMFONT_UNDER);
1.1       kristaps  241:        return(1);
                    242: }
                    243:
                    244:
                    245: /* ARGSUSED */
1.11      schwarze  246: static int
1.45      schwarze  247: pre_literal(DECL_ARGS)
1.11      schwarze  248: {
                    249:
1.45      schwarze  250:        term_newln(p);
1.53      schwarze  251:
                    252:        if (MAN_nf == n->tok)
1.45      schwarze  253:                mt->fl |= MANT_LITERAL;
1.53      schwarze  254:        else
1.45      schwarze  255:                mt->fl &= ~MANT_LITERAL;
                    256:
1.62      schwarze  257:        return(0);
1.11      schwarze  258: }
                    259:
                    260: /* ARGSUSED */
                    261: static int
1.51      schwarze  262: pre_alternate(DECL_ARGS)
1.1       kristaps  263: {
1.51      schwarze  264:        enum termfont            font[2];
                    265:        const struct man_node   *nn;
                    266:        int                      savelit, i;
1.1       kristaps  267:
1.51      schwarze  268:        switch (n->tok) {
                    269:        case (MAN_RB):
                    270:                font[0] = TERMFONT_NONE;
                    271:                font[1] = TERMFONT_BOLD;
                    272:                break;
                    273:        case (MAN_RI):
                    274:                font[0] = TERMFONT_NONE;
                    275:                font[1] = TERMFONT_UNDER;
                    276:                break;
                    277:        case (MAN_BR):
                    278:                font[0] = TERMFONT_BOLD;
                    279:                font[1] = TERMFONT_NONE;
                    280:                break;
                    281:        case (MAN_BI):
                    282:                font[0] = TERMFONT_BOLD;
                    283:                font[1] = TERMFONT_UNDER;
                    284:                break;
                    285:        case (MAN_IR):
                    286:                font[0] = TERMFONT_UNDER;
                    287:                font[1] = TERMFONT_NONE;
                    288:                break;
                    289:        case (MAN_IB):
                    290:                font[0] = TERMFONT_UNDER;
                    291:                font[1] = TERMFONT_BOLD;
                    292:                break;
                    293:        default:
                    294:                abort();
                    295:        }
1.17      schwarze  296:
1.51      schwarze  297:        savelit = MANT_LITERAL & mt->fl;
                    298:        mt->fl &= ~MANT_LITERAL;
1.17      schwarze  299:
1.51      schwarze  300:        for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
                    301:                term_fontrepl(p, font[i]);
                    302:                if (savelit && NULL == nn->next)
                    303:                        mt->fl |= MANT_LITERAL;
1.19      schwarze  304:                print_man_node(p, mt, nn, m);
1.51      schwarze  305:                if (nn->next)
1.1       kristaps  306:                        p->flags |= TERMP_NOSPACE;
                    307:        }
1.17      schwarze  308:
1.1       kristaps  309:        return(0);
                    310: }
                    311:
                    312: /* ARGSUSED */
                    313: static int
                    314: pre_B(DECL_ARGS)
                    315: {
                    316:
1.21      schwarze  317:        term_fontrepl(p, TERMFONT_BOLD);
1.1       kristaps  318:        return(1);
1.52      schwarze  319: }
                    320:
                    321: /* ARGSUSED */
                    322: static int
                    323: pre_ft(DECL_ARGS)
                    324: {
                    325:        const char      *cp;
                    326:
                    327:        if (NULL == n->child) {
                    328:                term_fontlast(p);
                    329:                return(0);
                    330:        }
                    331:
                    332:        cp = n->child->string;
                    333:        switch (*cp) {
                    334:        case ('4'):
                    335:                /* FALLTHROUGH */
                    336:        case ('3'):
                    337:                /* FALLTHROUGH */
                    338:        case ('B'):
                    339:                term_fontrepl(p, TERMFONT_BOLD);
                    340:                break;
                    341:        case ('2'):
                    342:                /* FALLTHROUGH */
                    343:        case ('I'):
                    344:                term_fontrepl(p, TERMFONT_UNDER);
                    345:                break;
                    346:        case ('P'):
                    347:                term_fontlast(p);
                    348:                break;
                    349:        case ('1'):
                    350:                /* FALLTHROUGH */
                    351:        case ('C'):
                    352:                /* FALLTHROUGH */
                    353:        case ('R'):
                    354:                term_fontrepl(p, TERMFONT_NONE);
                    355:                break;
                    356:        default:
                    357:                break;
                    358:        }
                    359:        return(0);
1.1       kristaps  360: }
                    361:
                    362: /* ARGSUSED */
                    363: static int
1.45      schwarze  364: pre_in(DECL_ARGS)
1.11      schwarze  365: {
1.45      schwarze  366:        int              len, less;
                    367:        size_t           v;
                    368:        const char      *cp;
                    369:
                    370:        term_newln(p);
                    371:
                    372:        if (NULL == n->child) {
                    373:                p->offset = mt->offset;
                    374:                return(0);
                    375:        }
1.11      schwarze  376:
1.45      schwarze  377:        cp = n->child->string;
                    378:        less = 0;
1.11      schwarze  379:
1.45      schwarze  380:        if ('-' == *cp)
                    381:                less = -1;
                    382:        else if ('+' == *cp)
                    383:                less = 1;
                    384:        else
                    385:                cp--;
                    386:
                    387:        if ((len = a2width(p, ++cp)) < 0)
                    388:                return(0);
                    389:
                    390:        v = (size_t)len;
                    391:
                    392:        if (less < 0)
                    393:                p->offset -= p->offset > v ? v : p->offset;
                    394:        else if (less > 0)
                    395:                p->offset += v;
                    396:        else
                    397:                p->offset = v;
1.59      schwarze  398:
                    399:        /* Don't let this creep beyond the right margin. */
                    400:
                    401:        if (p->offset > p->rmargin)
                    402:                p->offset = p->rmargin;
1.11      schwarze  403:
                    404:        return(0);
                    405: }
                    406:
                    407:
                    408: /* ARGSUSED */
                    409: static int
1.45      schwarze  410: pre_sp(DECL_ARGS)
1.8       schwarze  411: {
1.45      schwarze  412:        size_t           i, len;
                    413:
                    414:        switch (n->tok) {
                    415:        case (MAN_br):
                    416:                len = 0;
                    417:                break;
                    418:        default:
                    419:                len = n->child ? a2height(p, n->child->string) : 1;
                    420:                break;
                    421:        }
                    422:
                    423:        if (0 == len)
                    424:                term_newln(p);
                    425:        for (i = 0; i < len; i++)
                    426:                term_vspace(p);
1.8       schwarze  427:
                    428:        return(0);
                    429: }
                    430:
                    431:
                    432: /* ARGSUSED */
                    433: static int
1.11      schwarze  434: pre_HP(DECL_ARGS)
                    435: {
                    436:        size_t                   len;
                    437:        int                      ival;
                    438:        const struct man_node   *nn;
                    439:
                    440:        switch (n->type) {
                    441:        case (MAN_BLOCK):
1.18      schwarze  442:                print_bvspace(p, n);
1.11      schwarze  443:                return(1);
                    444:        case (MAN_BODY):
                    445:                p->flags |= TERMP_NOBREAK;
                    446:                p->flags |= TERMP_TWOSPACE;
                    447:                break;
                    448:        default:
                    449:                return(0);
                    450:        }
                    451:
1.13      schwarze  452:        len = mt->lmargin;
1.11      schwarze  453:        ival = -1;
                    454:
                    455:        /* Calculate offset. */
                    456:
                    457:        if (NULL != (nn = n->parent->head->child))
1.42      schwarze  458:                if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  459:                        len = (size_t)ival;
                    460:
                    461:        if (0 == len)
1.42      schwarze  462:                len = term_len(p, 1);
1.11      schwarze  463:
1.13      schwarze  464:        p->offset = mt->offset;
                    465:        p->rmargin = mt->offset + len;
1.11      schwarze  466:
                    467:        if (ival >= 0)
1.13      schwarze  468:                mt->lmargin = (size_t)ival;
1.11      schwarze  469:
                    470:        return(1);
                    471: }
                    472:
                    473:
                    474: /* ARGSUSED */
                    475: static void
                    476: post_HP(DECL_ARGS)
                    477: {
                    478:
                    479:        switch (n->type) {
                    480:        case (MAN_BLOCK):
                    481:                term_flushln(p);
                    482:                break;
                    483:        case (MAN_BODY):
                    484:                term_flushln(p);
                    485:                p->flags &= ~TERMP_NOBREAK;
                    486:                p->flags &= ~TERMP_TWOSPACE;
1.13      schwarze  487:                p->offset = mt->offset;
1.11      schwarze  488:                p->rmargin = p->maxrmargin;
                    489:                break;
                    490:        default:
                    491:                break;
                    492:        }
                    493: }
                    494:
                    495:
                    496: /* ARGSUSED */
                    497: static int
1.1       kristaps  498: pre_PP(DECL_ARGS)
                    499: {
                    500:
1.11      schwarze  501:        switch (n->type) {
                    502:        case (MAN_BLOCK):
1.42      schwarze  503:                mt->lmargin = term_len(p, INDENT);
1.18      schwarze  504:                print_bvspace(p, n);
1.11      schwarze  505:                break;
                    506:        default:
1.13      schwarze  507:                p->offset = mt->offset;
1.11      schwarze  508:                break;
                    509:        }
                    510:
1.54      schwarze  511:        return(MAN_HEAD != n->type);
1.1       kristaps  512: }
                    513:
                    514:
                    515: /* ARGSUSED */
                    516: static int
                    517: pre_IP(DECL_ARGS)
                    518: {
1.11      schwarze  519:        const struct man_node   *nn;
                    520:        size_t                   len;
1.57      schwarze  521:        int                      savelit, ival;
1.11      schwarze  522:
                    523:        switch (n->type) {
                    524:        case (MAN_BODY):
                    525:                p->flags |= TERMP_NOLPAD;
                    526:                p->flags |= TERMP_NOSPACE;
                    527:                break;
                    528:        case (MAN_HEAD):
                    529:                p->flags |= TERMP_NOBREAK;
                    530:                break;
                    531:        case (MAN_BLOCK):
1.18      schwarze  532:                print_bvspace(p, n);
1.11      schwarze  533:                /* FALLTHROUGH */
                    534:        default:
                    535:                return(1);
                    536:        }
                    537:
1.13      schwarze  538:        len = mt->lmargin;
1.11      schwarze  539:        ival = -1;
                    540:
1.57      schwarze  541:        /* Calculate the offset from the optional second argument. */
1.11      schwarze  542:        if (NULL != (nn = n->parent->head->child))
1.57      schwarze  543:                if (NULL != (nn = nn->next))
1.42      schwarze  544:                        if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  545:                                len = (size_t)ival;
                    546:
                    547:        switch (n->type) {
                    548:        case (MAN_HEAD):
                    549:                /* Handle zero-width lengths. */
                    550:                if (0 == len)
1.42      schwarze  551:                        len = term_len(p, 1);
1.11      schwarze  552:
1.13      schwarze  553:                p->offset = mt->offset;
                    554:                p->rmargin = mt->offset + len;
1.11      schwarze  555:                if (ival < 0)
                    556:                        break;
                    557:
                    558:                /* Set the saved left-margin. */
1.13      schwarze  559:                mt->lmargin = (size_t)ival;
1.1       kristaps  560:
1.57      schwarze  561:                savelit = MANT_LITERAL & mt->fl;
                    562:                mt->fl &= ~MANT_LITERAL;
                    563:
                    564:                if (n->child)
                    565:                        print_man_node(p, mt, n->child, m);
                    566:
                    567:                if (savelit)
                    568:                        mt->fl |= MANT_LITERAL;
                    569:
1.11      schwarze  570:                return(0);
                    571:        case (MAN_BODY):
1.13      schwarze  572:                p->offset = mt->offset + len;
1.11      schwarze  573:                p->rmargin = p->maxrmargin;
                    574:                break;
                    575:        default:
                    576:                break;
                    577:        }
1.1       kristaps  578:
1.11      schwarze  579:        return(1);
                    580: }
1.1       kristaps  581:
                    582:
1.11      schwarze  583: /* ARGSUSED */
                    584: static void
                    585: post_IP(DECL_ARGS)
                    586: {
1.4       schwarze  587:
1.11      schwarze  588:        switch (n->type) {
                    589:        case (MAN_HEAD):
                    590:                term_flushln(p);
                    591:                p->flags &= ~TERMP_NOBREAK;
                    592:                p->rmargin = p->maxrmargin;
                    593:                break;
                    594:        case (MAN_BODY):
1.57      schwarze  595:                term_newln(p);
1.11      schwarze  596:                p->flags &= ~TERMP_NOLPAD;
                    597:                break;
                    598:        default:
                    599:                break;
                    600:        }
1.1       kristaps  601: }
                    602:
                    603:
                    604: /* ARGSUSED */
                    605: static int
                    606: pre_TP(DECL_ARGS)
                    607: {
1.11      schwarze  608:        const struct man_node   *nn;
                    609:        size_t                   len;
1.57      schwarze  610:        int                      savelit, ival;
1.11      schwarze  611:
                    612:        switch (n->type) {
                    613:        case (MAN_HEAD):
                    614:                p->flags |= TERMP_NOBREAK;
                    615:                break;
                    616:        case (MAN_BODY):
                    617:                p->flags |= TERMP_NOLPAD;
                    618:                p->flags |= TERMP_NOSPACE;
                    619:                break;
                    620:        case (MAN_BLOCK):
1.18      schwarze  621:                print_bvspace(p, n);
1.11      schwarze  622:                /* FALLTHROUGH */
                    623:        default:
                    624:                return(1);
                    625:        }
                    626:
                    627:        len = (size_t)mt->lmargin;
                    628:        ival = -1;
                    629:
                    630:        /* Calculate offset. */
1.1       kristaps  631:
1.22      schwarze  632:        if (NULL != (nn = n->parent->head->child)) {
                    633:                while (nn && MAN_TEXT != nn->type)
                    634:                        nn = nn->next;
                    635:                if (nn && nn->next)
1.42      schwarze  636:                        if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  637:                                len = (size_t)ival;
1.22      schwarze  638:        }
1.8       schwarze  639:
1.11      schwarze  640:        switch (n->type) {
                    641:        case (MAN_HEAD):
                    642:                /* Handle zero-length properly. */
                    643:                if (0 == len)
1.42      schwarze  644:                        len = term_len(p, 1);
1.11      schwarze  645:
1.13      schwarze  646:                p->offset = mt->offset;
                    647:                p->rmargin = mt->offset + len;
1.11      schwarze  648:
1.57      schwarze  649:                savelit = MANT_LITERAL & mt->fl;
                    650:                mt->fl &= ~MANT_LITERAL;
                    651:
1.11      schwarze  652:                /* Don't print same-line elements. */
1.57      schwarze  653:                for (nn = n->child; nn; nn = nn->next)
1.11      schwarze  654:                        if (nn->line > n->line)
1.19      schwarze  655:                                print_man_node(p, mt, nn, m);
1.11      schwarze  656:
1.57      schwarze  657:                if (savelit)
                    658:                        mt->fl |= MANT_LITERAL;
                    659:
1.11      schwarze  660:                if (ival >= 0)
1.13      schwarze  661:                        mt->lmargin = (size_t)ival;
1.11      schwarze  662:
                    663:                return(0);
                    664:        case (MAN_BODY):
1.13      schwarze  665:                p->offset = mt->offset + len;
1.11      schwarze  666:                p->rmargin = p->maxrmargin;
                    667:                break;
                    668:        default:
                    669:                break;
                    670:        }
1.1       kristaps  671:
1.11      schwarze  672:        return(1);
                    673: }
1.1       kristaps  674:
                    675:
1.11      schwarze  676: /* ARGSUSED */
                    677: static void
                    678: post_TP(DECL_ARGS)
                    679: {
1.1       kristaps  680:
1.11      schwarze  681:        switch (n->type) {
                    682:        case (MAN_HEAD):
                    683:                term_flushln(p);
                    684:                p->flags &= ~TERMP_NOBREAK;
                    685:                p->flags &= ~TERMP_TWOSPACE;
                    686:                p->rmargin = p->maxrmargin;
                    687:                break;
                    688:        case (MAN_BODY):
1.57      schwarze  689:                term_newln(p);
1.11      schwarze  690:                p->flags &= ~TERMP_NOLPAD;
                    691:                break;
                    692:        default:
                    693:                break;
                    694:        }
1.1       kristaps  695: }
                    696:
                    697:
                    698: /* ARGSUSED */
                    699: static int
                    700: pre_SS(DECL_ARGS)
                    701: {
                    702:
1.11      schwarze  703:        switch (n->type) {
                    704:        case (MAN_BLOCK):
1.42      schwarze  705:                mt->lmargin = term_len(p, INDENT);
                    706:                mt->offset = term_len(p, INDENT);
1.11      schwarze  707:                /* If following a prior empty `SS', no vspace. */
                    708:                if (n->prev && MAN_SS == n->prev->tok)
                    709:                        if (NULL == n->prev->body->child)
                    710:                                break;
                    711:                if (NULL == n->prev)
                    712:                        break;
                    713:                term_vspace(p);
                    714:                break;
                    715:        case (MAN_HEAD):
1.21      schwarze  716:                term_fontrepl(p, TERMFONT_BOLD);
1.42      schwarze  717:                p->offset = term_len(p, HALFINDENT);
1.11      schwarze  718:                break;
                    719:        case (MAN_BODY):
1.13      schwarze  720:                p->offset = mt->offset;
1.11      schwarze  721:                break;
                    722:        default:
                    723:                break;
                    724:        }
                    725:
1.1       kristaps  726:        return(1);
                    727: }
                    728:
                    729:
                    730: /* ARGSUSED */
                    731: static void
                    732: post_SS(DECL_ARGS)
                    733: {
                    734:
1.11      schwarze  735:        switch (n->type) {
                    736:        case (MAN_HEAD):
                    737:                term_newln(p);
                    738:                break;
                    739:        case (MAN_BODY):
                    740:                term_newln(p);
                    741:                break;
                    742:        default:
                    743:                break;
                    744:        }
1.1       kristaps  745: }
                    746:
                    747:
                    748: /* ARGSUSED */
                    749: static int
                    750: pre_SH(DECL_ARGS)
                    751: {
                    752:
1.11      schwarze  753:        switch (n->type) {
                    754:        case (MAN_BLOCK):
1.42      schwarze  755:                mt->lmargin = term_len(p, INDENT);
                    756:                mt->offset = term_len(p, INDENT);
1.11      schwarze  757:                /* If following a prior empty `SH', no vspace. */
                    758:                if (n->prev && MAN_SH == n->prev->tok)
                    759:                        if (NULL == n->prev->body->child)
                    760:                                break;
1.29      schwarze  761:                /* If the first macro, no vspae. */
                    762:                if (NULL == n->prev)
                    763:                        break;
1.11      schwarze  764:                term_vspace(p);
                    765:                break;
                    766:        case (MAN_HEAD):
1.21      schwarze  767:                term_fontrepl(p, TERMFONT_BOLD);
1.11      schwarze  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:                break;
                    790:        case (MAN_BODY):
                    791:                term_newln(p);
                    792:                break;
                    793:        default:
1.13      schwarze  794:                break;
                    795:        }
                    796: }
                    797:
                    798:
                    799: /* ARGSUSED */
                    800: static int
                    801: pre_RS(DECL_ARGS)
                    802: {
                    803:        const struct man_node   *nn;
                    804:        int                      ival;
                    805:
                    806:        switch (n->type) {
                    807:        case (MAN_BLOCK):
                    808:                term_newln(p);
                    809:                return(1);
                    810:        case (MAN_HEAD):
                    811:                return(0);
                    812:        default:
                    813:                break;
                    814:        }
                    815:
                    816:        if (NULL == (nn = n->parent->head->child)) {
1.42      schwarze  817:                mt->offset = mt->lmargin + term_len(p, INDENT);
1.13      schwarze  818:                p->offset = mt->offset;
                    819:                return(1);
                    820:        }
                    821:
1.42      schwarze  822:        if ((ival = a2width(p, nn->string)) < 0)
1.13      schwarze  823:                return(1);
                    824:
1.42      schwarze  825:        mt->offset = term_len(p, INDENT) + (size_t)ival;
1.13      schwarze  826:        p->offset = mt->offset;
                    827:
                    828:        return(1);
                    829: }
                    830:
                    831:
                    832: /* ARGSUSED */
                    833: static void
                    834: post_RS(DECL_ARGS)
                    835: {
                    836:
                    837:        switch (n->type) {
                    838:        case (MAN_BLOCK):
1.42      schwarze  839:                mt->offset = mt->lmargin = term_len(p, INDENT);
1.13      schwarze  840:                break;
1.27      schwarze  841:        case (MAN_HEAD):
                    842:                break;
1.13      schwarze  843:        default:
                    844:                term_newln(p);
1.42      schwarze  845:                p->offset = term_len(p, INDENT);
1.11      schwarze  846:                break;
                    847:        }
1.47      schwarze  848: }
                    849:
                    850:
1.1       kristaps  851: static void
1.19      schwarze  852: print_man_node(DECL_ARGS)
1.1       kristaps  853: {
1.32      schwarze  854:        size_t           rm, rmax;
1.21      schwarze  855:        int              c;
1.1       kristaps  856:
                    857:        switch (n->type) {
                    858:        case(MAN_TEXT):
1.61      schwarze  859:                /*
                    860:                 * If we have a blank line, output a vertical space.
                    861:                 * If we have a space as the first character, break
                    862:                 * before printing the line's data.
                    863:                 */
1.60      schwarze  864:                if ('\0' == *n->string) {
1.1       kristaps  865:                        term_vspace(p);
1.61      schwarze  866:                        return;
                    867:                } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60      schwarze  868:                        term_newln(p);
1.21      schwarze  869:
1.1       kristaps  870:                term_word(p, n->string);
1.21      schwarze  871:
1.61      schwarze  872:                /*
                    873:                 * If we're in a literal context, make sure that words
                    874:                 * togehter on the same line stay together.  This is a
                    875:                 * POST-printing call, so we check the NEXT word.  Since
                    876:                 * -man doesn't have nested macros, we don't need to be
                    877:                 * more specific than this.
                    878:                 */
                    879:                if (MANT_LITERAL & mt->fl &&
                    880:                                (NULL == n->next ||
                    881:                                 n->next->line > n->line)) {
1.32      schwarze  882:                        rm = p->rmargin;
                    883:                        rmax = p->maxrmargin;
1.29      schwarze  884:                        p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11      schwarze  885:                        p->flags |= TERMP_NOSPACE;
                    886:                        term_flushln(p);
1.57      schwarze  887:                        p->flags &= ~TERMP_NOLPAD;
1.32      schwarze  888:                        p->rmargin = rm;
                    889:                        p->maxrmargin = rmax;
1.11      schwarze  890:                }
1.63      schwarze  891:
                    892:                if (MAN_EOS & n->flags)
                    893:                        p->flags |= TERMP_SENTENCE;
1.66    ! schwarze  894:                return;
        !           895:        case (MAN_EQN):
        !           896:                term_word(p, n->eqn->data);
1.61      schwarze  897:                return;
1.58      schwarze  898:        case (MAN_TBL):
1.61      schwarze  899:                /*
                    900:                 * Tables are preceded by a newline.  Then process a
                    901:                 * table line, which will cause line termination,
                    902:                 */
1.58      schwarze  903:                if (TBL_SPAN_FIRST & n->span->flags)
                    904:                        term_newln(p);
                    905:                term_tbl(p, n->span);
1.61      schwarze  906:                return;
1.1       kristaps  907:        default:
                    908:                break;
                    909:        }
                    910:
1.61      schwarze  911:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                    912:                term_fontrepl(p, TERMFONT_NONE);
                    913:
                    914:        c = 1;
                    915:        if (termacts[n->tok].pre)
                    916:                c = (*termacts[n->tok].pre)(p, mt, n, m);
                    917:
1.1       kristaps  918:        if (c && n->child)
1.21      schwarze  919:                print_man_nodelist(p, mt, n->child, m);
1.1       kristaps  920:
1.61      schwarze  921:        if (termacts[n->tok].post)
                    922:                (*termacts[n->tok].post)(p, mt, n, m);
                    923:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                    924:                term_fontrepl(p, TERMFONT_NONE);
1.30      schwarze  925:
                    926:        if (MAN_EOS & n->flags)
                    927:                p->flags |= TERMP_SENTENCE;
1.1       kristaps  928: }
                    929:
                    930:
                    931: static void
1.21      schwarze  932: print_man_nodelist(DECL_ARGS)
1.1       kristaps  933: {
1.11      schwarze  934:
1.19      schwarze  935:        print_man_node(p, mt, n, m);
1.1       kristaps  936:        if ( ! n->next)
                    937:                return;
1.21      schwarze  938:        print_man_nodelist(p, mt, n->next, m);
1.1       kristaps  939: }
                    940:
                    941:
                    942: static void
1.41      schwarze  943: print_man_foot(struct termp *p, const void *arg)
1.1       kristaps  944: {
1.41      schwarze  945:        const struct man_meta *meta;
                    946:
                    947:        meta = (const struct man_meta *)arg;
1.21      schwarze  948:
                    949:        term_fontrepl(p, TERMFONT_NONE);
1.1       kristaps  950:
1.38      schwarze  951:        term_vspace(p);
                    952:        term_vspace(p);
1.1       kristaps  953:        term_vspace(p);
                    954:
                    955:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.65      schwarze  956:        p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
1.1       kristaps  957:        p->offset = 0;
1.46      schwarze  958:
                    959:        /* term_strlen() can return zero. */
                    960:        if (p->rmargin == p->maxrmargin)
                    961:                p->rmargin--;
1.1       kristaps  962:
                    963:        if (meta->source)
                    964:                term_word(p, meta->source);
                    965:        if (meta->source)
                    966:                term_word(p, "");
                    967:        term_flushln(p);
                    968:
                    969:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    970:        p->offset = p->rmargin;
                    971:        p->rmargin = p->maxrmargin;
                    972:        p->flags &= ~TERMP_NOBREAK;
                    973:
1.65      schwarze  974:        term_word(p, meta->date);
1.1       kristaps  975:        term_flushln(p);
                    976: }
                    977:
                    978:
                    979: static void
1.41      schwarze  980: print_man_head(struct termp *p, const void *arg)
1.1       kristaps  981: {
1.20      schwarze  982:        char            buf[BUFSIZ], title[BUFSIZ];
1.25      schwarze  983:        size_t          buflen, titlen;
1.41      schwarze  984:        const struct man_meta *m;
                    985:
                    986:        m = (const struct man_meta *)arg;
1.1       kristaps  987:
1.29      schwarze  988:        /*
                    989:         * Note that old groff would spit out some spaces before the
                    990:         * header.  We discontinue this strange behaviour, but at one
                    991:         * point we did so here.
                    992:         */
                    993:
1.1       kristaps  994:        p->rmargin = p->maxrmargin;
1.27      schwarze  995:
1.1       kristaps  996:        p->offset = 0;
1.20      schwarze  997:        buf[0] = title[0] = '\0';
1.1       kristaps  998:
1.20      schwarze  999:        if (m->vol)
                   1000:                strlcpy(buf, m->vol, BUFSIZ);
1.42      schwarze 1001:        buflen = term_strlen(p, buf);
1.1       kristaps 1002:
1.31      schwarze 1003:        snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42      schwarze 1004:        titlen = term_strlen(p, title);
1.1       kristaps 1005:
                   1006:        p->offset = 0;
1.25      schwarze 1007:        p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42      schwarze 1008:            (p->maxrmargin -
                   1009:             term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25      schwarze 1010:            p->maxrmargin - buflen;
1.1       kristaps 1011:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                   1012:
                   1013:        term_word(p, title);
                   1014:        term_flushln(p);
                   1015:
                   1016:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                   1017:        p->offset = p->rmargin;
1.25      schwarze 1018:        p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
                   1019:            p->maxrmargin - titlen : p->maxrmargin;
1.1       kristaps 1020:
                   1021:        term_word(p, buf);
                   1022:        term_flushln(p);
                   1023:
                   1024:        p->flags &= ~TERMP_NOBREAK;
1.25      schwarze 1025:        if (p->rmargin + titlen <= p->maxrmargin) {
                   1026:                p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                   1027:                p->offset = p->rmargin;
                   1028:                p->rmargin = p->maxrmargin;
                   1029:                term_word(p, title);
                   1030:                term_flushln(p);
                   1031:        }
1.1       kristaps 1032:
                   1033:        p->rmargin = p->maxrmargin;
                   1034:        p->offset = 0;
                   1035:        p->flags &= ~TERMP_NOSPACE;
1.29      schwarze 1036:
                   1037:        /*
                   1038:         * Groff likes to have some leading spaces before content.  Well
                   1039:         * that's fine by me.
                   1040:         */
                   1041:
                   1042:        term_vspace(p);
                   1043:        term_vspace(p);
                   1044:        term_vspace(p);
1.1       kristaps 1045: }