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

1.108   ! schwarze    1: /*     $OpenBSD: man_term.c,v 1.107 2014/10/20 15:49:45 schwarze Exp $ */
1.1       kristaps    2: /*
1.84      schwarze    3:  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.94      schwarze    4:  * Copyright (c) 2010-2014 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.101     schwarze   27: #include "mandoc_aux.h"
1.18      schwarze   28: #include "out.h"
                     29: #include "man.h"
1.1       kristaps   30: #include "term.h"
1.18      schwarze   31: #include "main.h"
1.10      schwarze   32:
1.70      schwarze   33: #define        MAXMARGINS        64 /* maximum number of indented scopes */
1.1       kristaps   34:
1.11      schwarze   35: struct mtermp {
                     36:        int               fl;
                     37: #define        MANT_LITERAL     (1 << 0)
1.70      schwarze   38:        size_t            lmargin[MAXMARGINS]; /* margins (incl. visible page) */
                     39:        int               lmargincur; /* index of current margin */
                     40:        int               lmarginsz; /* actual number of nested margins */
                     41:        size_t            offset; /* default offset to visible page */
1.88      schwarze   42:        int               pardist; /* vert. space before par., unit: [v] */
1.11      schwarze   43: };
                     44:
1.100     schwarze   45: #define        DECL_ARGS         struct termp *p, \
1.11      schwarze   46:                          struct mtermp *mt, \
1.1       kristaps   47:                          const struct man_node *n, \
1.89      schwarze   48:                          const struct man_meta *meta
1.1       kristaps   49:
                     50: struct termact {
                     51:        int             (*pre)(DECL_ARGS);
                     52:        void            (*post)(DECL_ARGS);
1.26      schwarze   53:        int               flags;
                     54: #define        MAN_NOTEXT       (1 << 0) /* Never has text children. */
1.1       kristaps   55: };
                     56:
1.42      schwarze   57: static int               a2width(const struct termp *, const char *);
                     58: static size_t            a2height(const struct termp *, const char *);
1.18      schwarze   59:
1.21      schwarze   60: static void              print_man_nodelist(DECL_ARGS);
1.19      schwarze   61: static void              print_man_node(DECL_ARGS);
1.41      schwarze   62: static void              print_man_head(struct termp *, const void *);
                     63: static void              print_man_foot(struct termp *, const void *);
1.100     schwarze   64: static void              print_bvspace(struct termp *,
1.88      schwarze   65:                                const struct man_node *, int);
1.18      schwarze   66:
1.1       kristaps   67: static int               pre_B(DECL_ARGS);
1.11      schwarze   68: static int               pre_HP(DECL_ARGS);
1.1       kristaps   69: static int               pre_I(DECL_ARGS);
                     70: static int               pre_IP(DECL_ARGS);
1.82      schwarze   71: static int               pre_OP(DECL_ARGS);
1.88      schwarze   72: static int               pre_PD(DECL_ARGS);
1.1       kristaps   73: static int               pre_PP(DECL_ARGS);
1.13      schwarze   74: static int               pre_RS(DECL_ARGS);
1.1       kristaps   75: static int               pre_SH(DECL_ARGS);
                     76: static int               pre_SS(DECL_ARGS);
                     77: static int               pre_TP(DECL_ARGS);
1.91      schwarze   78: static int               pre_UR(DECL_ARGS);
1.82      schwarze   79: static int               pre_alternate(DECL_ARGS);
                     80: static int               pre_ft(DECL_ARGS);
1.14      schwarze   81: static int               pre_ign(DECL_ARGS);
1.45      schwarze   82: static int               pre_in(DECL_ARGS);
                     83: static int               pre_literal(DECL_ARGS);
1.97      schwarze   84: static int               pre_ll(DECL_ARGS);
1.11      schwarze   85: static int               pre_sp(DECL_ARGS);
1.1       kristaps   86:
1.11      schwarze   87: static void              post_IP(DECL_ARGS);
                     88: static void              post_HP(DECL_ARGS);
1.13      schwarze   89: static void              post_RS(DECL_ARGS);
1.1       kristaps   90: static void              post_SH(DECL_ARGS);
                     91: static void              post_SS(DECL_ARGS);
1.11      schwarze   92: static void              post_TP(DECL_ARGS);
1.91      schwarze   93: static void              post_UR(DECL_ARGS);
1.1       kristaps   94:
1.17      schwarze   95: static const struct termact termacts[MAN_MAX] = {
1.45      schwarze   96:        { pre_sp, NULL, MAN_NOTEXT }, /* br */
1.26      schwarze   97:        { NULL, NULL, 0 }, /* TH */
                     98:        { pre_SH, post_SH, 0 }, /* SH */
                     99:        { pre_SS, post_SS, 0 }, /* SS */
                    100:        { pre_TP, post_TP, 0 }, /* TP */
                    101:        { pre_PP, NULL, 0 }, /* LP */
                    102:        { pre_PP, NULL, 0 }, /* PP */
                    103:        { pre_PP, NULL, 0 }, /* P */
                    104:        { pre_IP, post_IP, 0 }, /* IP */
1.100     schwarze  105:        { pre_HP, post_HP, 0 }, /* HP */
1.26      schwarze  106:        { NULL, NULL, 0 }, /* SM */
                    107:        { pre_B, NULL, 0 }, /* SB */
1.51      schwarze  108:        { pre_alternate, NULL, 0 }, /* BI */
                    109:        { pre_alternate, NULL, 0 }, /* IB */
                    110:        { pre_alternate, NULL, 0 }, /* BR */
                    111:        { pre_alternate, NULL, 0 }, /* RB */
1.26      schwarze  112:        { NULL, NULL, 0 }, /* R */
                    113:        { pre_B, NULL, 0 }, /* B */
                    114:        { pre_I, NULL, 0 }, /* I */
1.51      schwarze  115:        { pre_alternate, NULL, 0 }, /* IR */
                    116:        { pre_alternate, NULL, 0 }, /* RI */
1.62      schwarze  117:        { pre_ign, NULL, MAN_NOTEXT }, /* na */
1.26      schwarze  118:        { pre_sp, NULL, MAN_NOTEXT }, /* sp */
1.45      schwarze  119:        { pre_literal, NULL, 0 }, /* nf */
                    120:        { pre_literal, NULL, 0 }, /* fi */
1.26      schwarze  121:        { NULL, NULL, 0 }, /* RE */
                    122:        { pre_RS, post_RS, 0 }, /* RS */
                    123:        { pre_ign, NULL, 0 }, /* DT */
                    124:        { pre_ign, NULL, 0 }, /* UC */
1.88      schwarze  125:        { pre_PD, NULL, MAN_NOTEXT }, /* PD */
1.36      schwarze  126:        { pre_ign, NULL, 0 }, /* AT */
1.45      schwarze  127:        { pre_in, NULL, MAN_NOTEXT }, /* in */
1.52      schwarze  128:        { pre_ft, NULL, MAN_NOTEXT }, /* ft */
1.82      schwarze  129:        { pre_OP, NULL, 0 }, /* OP */
1.83      schwarze  130:        { pre_literal, NULL, 0 }, /* EX */
                    131:        { pre_literal, NULL, 0 }, /* EE */
1.91      schwarze  132:        { pre_UR, post_UR, 0 }, /* UR */
                    133:        { NULL, NULL, 0 }, /* UE */
1.97      schwarze  134:        { pre_ll, NULL, MAN_NOTEXT }, /* ll */
1.1       kristaps  135: };
1.11      schwarze  136:
1.1       kristaps  137:
1.16      schwarze  138: void
1.18      schwarze  139: terminal_man(void *arg, const struct man *man)
1.1       kristaps  140: {
1.18      schwarze  141:        struct termp            *p;
1.89      schwarze  142:        const struct man_meta   *meta;
1.104     schwarze  143:        struct man_node         *n;
1.18      schwarze  144:        struct mtermp            mt;
                    145:
                    146:        p = (struct termp *)arg;
                    147:
1.39      schwarze  148:        p->overstep = 0;
1.104     schwarze  149:        p->rmargin = p->maxrmargin = p->defrmargin;
1.42      schwarze  150:        p->tabwidth = term_len(p, 5);
1.18      schwarze  151:
1.104     schwarze  152:        n = man_node(man)->child;
1.89      schwarze  153:        meta = man_meta(man);
1.1       kristaps  154:
1.70      schwarze  155:        memset(&mt, 0, sizeof(struct mtermp));
                    156:
1.77      schwarze  157:        mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
                    158:        mt.offset = term_len(p, p->defindent);
1.88      schwarze  159:        mt.pardist = 1;
1.11      schwarze  160:
1.104     schwarze  161:        if (p->synopsisonly) {
                    162:                while (n != NULL) {
                    163:                        if (n->tok == MAN_SH &&
                    164:                            n->child->child->type == MAN_TEXT &&
                    165:                            !strcmp(n->child->child->string, "SYNOPSIS")) {
                    166:                                if (n->child->next->child != NULL)
                    167:                                        print_man_nodelist(p, &mt,
                    168:                                            n->child->next->child, meta);
                    169:                                term_newln(p);
                    170:                                break;
                    171:                        }
                    172:                        n = n->next;
                    173:                }
                    174:        } else {
                    175:                if (p->defindent == 0)
                    176:                        p->defindent = 7;
                    177:                term_begin(p, print_man_head, print_man_foot, meta);
                    178:                p->flags |= TERMP_NOSPACE;
                    179:                if (n != NULL)
                    180:                        print_man_nodelist(p, &mt, n, meta);
                    181:                term_end(p);
                    182:        }
1.1       kristaps  183: }
                    184:
                    185:
1.42      schwarze  186: static size_t
                    187: a2height(const struct termp *p, const char *cp)
1.11      schwarze  188: {
1.18      schwarze  189:        struct roffsu    su;
1.11      schwarze  190:
1.42      schwarze  191:        if ( ! a2roffsu(cp, &su, SCALE_VS))
1.69      schwarze  192:                SCALE_VS_INIT(&su, atoi(cp));
1.11      schwarze  193:
1.42      schwarze  194:        return(term_vspan(p, &su));
1.11      schwarze  195: }
                    196:
                    197: static int
1.42      schwarze  198: a2width(const struct termp *p, const char *cp)
1.11      schwarze  199: {
1.18      schwarze  200:        struct roffsu    su;
1.11      schwarze  201:
1.42      schwarze  202:        if ( ! a2roffsu(cp, &su, SCALE_BU))
1.18      schwarze  203:                return(-1);
                    204:
1.42      schwarze  205:        return((int)term_hspan(p, &su));
1.18      schwarze  206: }
1.11      schwarze  207:
1.69      schwarze  208: /*
                    209:  * Printing leading vertical space before a block.
                    210:  * This is used for the paragraph macros.
                    211:  * The rules are pretty simple, since there's very little nesting going
                    212:  * on here.  Basically, if we're the first within another block (SS/SH),
                    213:  * then don't emit vertical space.  If we are (RS), then do.  If not the
                    214:  * first, print it.
                    215:  */
1.18      schwarze  216: static void
1.88      schwarze  217: print_bvspace(struct termp *p, const struct man_node *n, int pardist)
1.18      schwarze  218: {
1.88      schwarze  219:        int      i;
1.69      schwarze  220:
1.18      schwarze  221:        term_newln(p);
1.64      schwarze  222:
1.69      schwarze  223:        if (n->body && n->body->child)
                    224:                if (MAN_TBL == n->body->child->type)
                    225:                        return;
1.11      schwarze  226:
1.69      schwarze  227:        if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
                    228:                if (NULL == n->prev)
                    229:                        return;
1.11      schwarze  230:
1.88      schwarze  231:        for (i = 0; i < pardist; i++)
                    232:                term_vspace(p);
1.14      schwarze  233: }
                    234:
1.100     schwarze  235:
1.14      schwarze  236: static int
                    237: pre_ign(DECL_ARGS)
                    238: {
                    239:
1.97      schwarze  240:        return(0);
                    241: }
                    242:
                    243: static int
                    244: pre_ll(DECL_ARGS)
                    245: {
                    246:
1.98      schwarze  247:        term_setwidth(p, n->nchild ? n->child->string : NULL);
1.14      schwarze  248:        return(0);
1.11      schwarze  249: }
                    250:
1.1       kristaps  251: static int
                    252: pre_I(DECL_ARGS)
                    253: {
                    254:
1.21      schwarze  255:        term_fontrepl(p, TERMFONT_UNDER);
1.1       kristaps  256:        return(1);
                    257: }
                    258:
1.11      schwarze  259: static int
1.45      schwarze  260: pre_literal(DECL_ARGS)
1.11      schwarze  261: {
                    262:
1.45      schwarze  263:        term_newln(p);
1.53      schwarze  264:
1.83      schwarze  265:        if (MAN_nf == n->tok || MAN_EX == n->tok)
1.45      schwarze  266:                mt->fl |= MANT_LITERAL;
1.53      schwarze  267:        else
1.45      schwarze  268:                mt->fl &= ~MANT_LITERAL;
                    269:
1.72      schwarze  270:        /*
                    271:         * Unlike .IP and .TP, .HP does not have a HEAD.
                    272:         * So in case a second call to term_flushln() is needed,
                    273:         * indentation has to be set up explicitly.
                    274:         */
                    275:        if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
1.76      schwarze  276:                p->offset = p->rmargin;
1.72      schwarze  277:                p->rmargin = p->maxrmargin;
1.93      schwarze  278:                p->trailspace = 0;
1.99      schwarze  279:                p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.72      schwarze  280:                p->flags |= TERMP_NOSPACE;
                    281:        }
                    282:
1.62      schwarze  283:        return(0);
1.11      schwarze  284: }
                    285:
                    286: static int
1.88      schwarze  287: pre_PD(DECL_ARGS)
                    288: {
                    289:
                    290:        n = n->child;
                    291:        if (0 == n) {
                    292:                mt->pardist = 1;
                    293:                return(0);
                    294:        }
                    295:        assert(MAN_TEXT == n->type);
                    296:        mt->pardist = atoi(n->string);
                    297:        return(0);
                    298: }
                    299:
                    300: static int
1.51      schwarze  301: pre_alternate(DECL_ARGS)
1.1       kristaps  302: {
1.51      schwarze  303:        enum termfont            font[2];
                    304:        const struct man_node   *nn;
                    305:        int                      savelit, i;
1.1       kristaps  306:
1.51      schwarze  307:        switch (n->tok) {
1.100     schwarze  308:        case MAN_RB:
1.51      schwarze  309:                font[0] = TERMFONT_NONE;
                    310:                font[1] = TERMFONT_BOLD;
                    311:                break;
1.100     schwarze  312:        case MAN_RI:
1.51      schwarze  313:                font[0] = TERMFONT_NONE;
                    314:                font[1] = TERMFONT_UNDER;
                    315:                break;
1.100     schwarze  316:        case MAN_BR:
1.51      schwarze  317:                font[0] = TERMFONT_BOLD;
                    318:                font[1] = TERMFONT_NONE;
                    319:                break;
1.100     schwarze  320:        case MAN_BI:
1.51      schwarze  321:                font[0] = TERMFONT_BOLD;
                    322:                font[1] = TERMFONT_UNDER;
                    323:                break;
1.100     schwarze  324:        case MAN_IR:
1.51      schwarze  325:                font[0] = TERMFONT_UNDER;
                    326:                font[1] = TERMFONT_NONE;
                    327:                break;
1.100     schwarze  328:        case MAN_IB:
1.51      schwarze  329:                font[0] = TERMFONT_UNDER;
                    330:                font[1] = TERMFONT_BOLD;
                    331:                break;
                    332:        default:
                    333:                abort();
                    334:        }
1.17      schwarze  335:
1.51      schwarze  336:        savelit = MANT_LITERAL & mt->fl;
                    337:        mt->fl &= ~MANT_LITERAL;
1.17      schwarze  338:
1.51      schwarze  339:        for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
                    340:                term_fontrepl(p, font[i]);
                    341:                if (savelit && NULL == nn->next)
                    342:                        mt->fl |= MANT_LITERAL;
1.89      schwarze  343:                print_man_node(p, mt, nn, meta);
1.51      schwarze  344:                if (nn->next)
1.1       kristaps  345:                        p->flags |= TERMP_NOSPACE;
                    346:        }
1.17      schwarze  347:
1.1       kristaps  348:        return(0);
                    349: }
                    350:
                    351: static int
                    352: pre_B(DECL_ARGS)
                    353: {
                    354:
1.21      schwarze  355:        term_fontrepl(p, TERMFONT_BOLD);
1.1       kristaps  356:        return(1);
1.82      schwarze  357: }
                    358:
                    359: static int
                    360: pre_OP(DECL_ARGS)
                    361: {
                    362:
                    363:        term_word(p, "[");
                    364:        p->flags |= TERMP_NOSPACE;
                    365:
                    366:        if (NULL != (n = n->child)) {
                    367:                term_fontrepl(p, TERMFONT_BOLD);
                    368:                term_word(p, n->string);
                    369:        }
                    370:        if (NULL != n && NULL != n->next) {
                    371:                term_fontrepl(p, TERMFONT_UNDER);
                    372:                term_word(p, n->next->string);
                    373:        }
                    374:
                    375:        term_fontrepl(p, TERMFONT_NONE);
                    376:        p->flags |= TERMP_NOSPACE;
                    377:        term_word(p, "]");
                    378:        return(0);
1.52      schwarze  379: }
                    380:
                    381: static int
                    382: pre_ft(DECL_ARGS)
                    383: {
                    384:        const char      *cp;
                    385:
                    386:        if (NULL == n->child) {
                    387:                term_fontlast(p);
                    388:                return(0);
                    389:        }
                    390:
                    391:        cp = n->child->string;
                    392:        switch (*cp) {
1.100     schwarze  393:        case '4':
1.52      schwarze  394:                /* FALLTHROUGH */
1.100     schwarze  395:        case '3':
1.52      schwarze  396:                /* FALLTHROUGH */
1.100     schwarze  397:        case 'B':
1.52      schwarze  398:                term_fontrepl(p, TERMFONT_BOLD);
                    399:                break;
1.100     schwarze  400:        case '2':
1.52      schwarze  401:                /* FALLTHROUGH */
1.100     schwarze  402:        case 'I':
1.52      schwarze  403:                term_fontrepl(p, TERMFONT_UNDER);
                    404:                break;
1.100     schwarze  405:        case 'P':
1.52      schwarze  406:                term_fontlast(p);
                    407:                break;
1.100     schwarze  408:        case '1':
1.52      schwarze  409:                /* FALLTHROUGH */
1.100     schwarze  410:        case 'C':
1.52      schwarze  411:                /* FALLTHROUGH */
1.100     schwarze  412:        case 'R':
1.52      schwarze  413:                term_fontrepl(p, TERMFONT_NONE);
                    414:                break;
                    415:        default:
                    416:                break;
                    417:        }
                    418:        return(0);
1.1       kristaps  419: }
                    420:
                    421: static int
1.45      schwarze  422: pre_in(DECL_ARGS)
1.11      schwarze  423: {
1.45      schwarze  424:        int              len, less;
                    425:        size_t           v;
                    426:        const char      *cp;
                    427:
                    428:        term_newln(p);
                    429:
                    430:        if (NULL == n->child) {
                    431:                p->offset = mt->offset;
                    432:                return(0);
                    433:        }
1.11      schwarze  434:
1.45      schwarze  435:        cp = n->child->string;
                    436:        less = 0;
1.11      schwarze  437:
1.45      schwarze  438:        if ('-' == *cp)
                    439:                less = -1;
                    440:        else if ('+' == *cp)
                    441:                less = 1;
                    442:        else
                    443:                cp--;
                    444:
                    445:        if ((len = a2width(p, ++cp)) < 0)
                    446:                return(0);
                    447:
                    448:        v = (size_t)len;
                    449:
                    450:        if (less < 0)
                    451:                p->offset -= p->offset > v ? v : p->offset;
                    452:        else if (less > 0)
                    453:                p->offset += v;
1.100     schwarze  454:        else
1.45      schwarze  455:                p->offset = v;
1.59      schwarze  456:
                    457:        /* Don't let this creep beyond the right margin. */
                    458:
                    459:        if (p->offset > p->rmargin)
                    460:                p->offset = p->rmargin;
1.11      schwarze  461:
                    462:        return(0);
                    463: }
                    464:
                    465: static int
1.45      schwarze  466: pre_sp(DECL_ARGS)
1.8       schwarze  467: {
1.85      schwarze  468:        char            *s;
1.45      schwarze  469:        size_t           i, len;
1.85      schwarze  470:        int              neg;
1.45      schwarze  471:
1.69      schwarze  472:        if ((NULL == n->prev && n->parent)) {
1.86      schwarze  473:                switch (n->parent->tok) {
1.100     schwarze  474:                case MAN_SH:
1.86      schwarze  475:                        /* FALLTHROUGH */
1.100     schwarze  476:                case MAN_SS:
1.86      schwarze  477:                        /* FALLTHROUGH */
1.100     schwarze  478:                case MAN_PP:
1.86      schwarze  479:                        /* FALLTHROUGH */
1.100     schwarze  480:                case MAN_LP:
1.86      schwarze  481:                        /* FALLTHROUGH */
1.100     schwarze  482:                case MAN_P:
1.86      schwarze  483:                        /* FALLTHROUGH */
1.69      schwarze  484:                        return(0);
1.86      schwarze  485:                default:
                    486:                        break;
                    487:                }
1.69      schwarze  488:        }
                    489:
1.85      schwarze  490:        neg = 0;
1.45      schwarze  491:        switch (n->tok) {
1.100     schwarze  492:        case MAN_br:
1.45      schwarze  493:                len = 0;
                    494:                break;
                    495:        default:
1.85      schwarze  496:                if (NULL == n->child) {
                    497:                        len = 1;
                    498:                        break;
                    499:                }
                    500:                s = n->child->string;
                    501:                if ('-' == *s) {
                    502:                        neg = 1;
                    503:                        s++;
                    504:                }
                    505:                len = a2height(p, s);
1.45      schwarze  506:                break;
                    507:        }
                    508:
                    509:        if (0 == len)
                    510:                term_newln(p);
1.85      schwarze  511:        else if (neg)
                    512:                p->skipvsp += len;
                    513:        else
                    514:                for (i = 0; i < len; i++)
                    515:                        term_vspace(p);
1.8       schwarze  516:
                    517:        return(0);
                    518: }
                    519:
                    520: static int
1.11      schwarze  521: pre_HP(DECL_ARGS)
                    522: {
1.72      schwarze  523:        size_t                   len, one;
1.11      schwarze  524:        int                      ival;
                    525:        const struct man_node   *nn;
                    526:
                    527:        switch (n->type) {
1.100     schwarze  528:        case MAN_BLOCK:
1.88      schwarze  529:                print_bvspace(p, n, mt->pardist);
1.11      schwarze  530:                return(1);
1.100     schwarze  531:        case MAN_BODY:
1.11      schwarze  532:                break;
                    533:        default:
                    534:                return(0);
                    535:        }
                    536:
1.84      schwarze  537:        if ( ! (MANT_LITERAL & mt->fl)) {
1.99      schwarze  538:                p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.93      schwarze  539:                p->trailspace = 2;
1.84      schwarze  540:        }
                    541:
1.70      schwarze  542:        len = mt->lmargin[mt->lmargincur];
1.11      schwarze  543:        ival = -1;
                    544:
                    545:        /* Calculate offset. */
                    546:
                    547:        if (NULL != (nn = n->parent->head->child))
1.42      schwarze  548:                if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  549:                        len = (size_t)ival;
                    550:
1.72      schwarze  551:        one = term_len(p, 1);
1.76      schwarze  552:        if (len < one)
1.72      schwarze  553:                len = one;
1.11      schwarze  554:
1.13      schwarze  555:        p->offset = mt->offset;
                    556:        p->rmargin = mt->offset + len;
1.11      schwarze  557:
                    558:        if (ival >= 0)
1.70      schwarze  559:                mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11      schwarze  560:
                    561:        return(1);
                    562: }
                    563:
                    564: static void
                    565: post_HP(DECL_ARGS)
                    566: {
                    567:
                    568:        switch (n->type) {
1.100     schwarze  569:        case MAN_BODY:
1.90      schwarze  570:                term_newln(p);
1.99      schwarze  571:                p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
1.93      schwarze  572:                p->trailspace = 0;
1.13      schwarze  573:                p->offset = mt->offset;
1.11      schwarze  574:                p->rmargin = p->maxrmargin;
                    575:                break;
                    576:        default:
                    577:                break;
                    578:        }
                    579: }
                    580:
                    581: static int
1.1       kristaps  582: pre_PP(DECL_ARGS)
                    583: {
                    584:
1.11      schwarze  585:        switch (n->type) {
1.100     schwarze  586:        case MAN_BLOCK:
1.77      schwarze  587:                mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
1.88      schwarze  588:                print_bvspace(p, n, mt->pardist);
1.11      schwarze  589:                break;
                    590:        default:
1.13      schwarze  591:                p->offset = mt->offset;
1.11      schwarze  592:                break;
                    593:        }
                    594:
1.54      schwarze  595:        return(MAN_HEAD != n->type);
1.1       kristaps  596: }
                    597:
                    598: static int
                    599: pre_IP(DECL_ARGS)
                    600: {
1.11      schwarze  601:        const struct man_node   *nn;
                    602:        size_t                   len;
1.57      schwarze  603:        int                      savelit, ival;
1.11      schwarze  604:
                    605:        switch (n->type) {
1.100     schwarze  606:        case MAN_BODY:
1.11      schwarze  607:                p->flags |= TERMP_NOSPACE;
                    608:                break;
1.100     schwarze  609:        case MAN_HEAD:
1.11      schwarze  610:                p->flags |= TERMP_NOBREAK;
1.93      schwarze  611:                p->trailspace = 1;
1.11      schwarze  612:                break;
1.100     schwarze  613:        case MAN_BLOCK:
1.88      schwarze  614:                print_bvspace(p, n, mt->pardist);
1.11      schwarze  615:                /* FALLTHROUGH */
                    616:        default:
                    617:                return(1);
                    618:        }
                    619:
1.70      schwarze  620:        len = mt->lmargin[mt->lmargincur];
1.11      schwarze  621:        ival = -1;
                    622:
1.57      schwarze  623:        /* Calculate the offset from the optional second argument. */
1.11      schwarze  624:        if (NULL != (nn = n->parent->head->child))
1.57      schwarze  625:                if (NULL != (nn = nn->next))
1.42      schwarze  626:                        if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  627:                                len = (size_t)ival;
                    628:
                    629:        switch (n->type) {
1.100     schwarze  630:        case MAN_HEAD:
1.11      schwarze  631:                /* Handle zero-width lengths. */
                    632:                if (0 == len)
1.42      schwarze  633:                        len = term_len(p, 1);
1.11      schwarze  634:
1.13      schwarze  635:                p->offset = mt->offset;
                    636:                p->rmargin = mt->offset + len;
1.11      schwarze  637:                if (ival < 0)
                    638:                        break;
                    639:
                    640:                /* Set the saved left-margin. */
1.70      schwarze  641:                mt->lmargin[mt->lmargincur] = (size_t)ival;
1.1       kristaps  642:
1.57      schwarze  643:                savelit = MANT_LITERAL & mt->fl;
                    644:                mt->fl &= ~MANT_LITERAL;
                    645:
                    646:                if (n->child)
1.89      schwarze  647:                        print_man_node(p, mt, n->child, meta);
1.57      schwarze  648:
                    649:                if (savelit)
                    650:                        mt->fl |= MANT_LITERAL;
                    651:
1.11      schwarze  652:                return(0);
1.100     schwarze  653:        case MAN_BODY:
1.13      schwarze  654:                p->offset = mt->offset + len;
1.94      schwarze  655:                p->rmargin = p->maxrmargin > p->offset ?
1.100     schwarze  656:                    p->maxrmargin : p->offset;
1.11      schwarze  657:                break;
                    658:        default:
                    659:                break;
                    660:        }
1.1       kristaps  661:
1.11      schwarze  662:        return(1);
                    663: }
1.1       kristaps  664:
1.11      schwarze  665: static void
                    666: post_IP(DECL_ARGS)
                    667: {
1.4       schwarze  668:
1.11      schwarze  669:        switch (n->type) {
1.100     schwarze  670:        case MAN_HEAD:
1.11      schwarze  671:                term_flushln(p);
                    672:                p->flags &= ~TERMP_NOBREAK;
1.93      schwarze  673:                p->trailspace = 0;
1.11      schwarze  674:                p->rmargin = p->maxrmargin;
                    675:                break;
1.100     schwarze  676:        case MAN_BODY:
1.57      schwarze  677:                term_newln(p);
1.92      schwarze  678:                p->offset = mt->offset;
1.11      schwarze  679:                break;
                    680:        default:
                    681:                break;
                    682:        }
1.1       kristaps  683: }
                    684:
                    685: static int
                    686: pre_TP(DECL_ARGS)
                    687: {
1.11      schwarze  688:        const struct man_node   *nn;
                    689:        size_t                   len;
1.57      schwarze  690:        int                      savelit, ival;
1.11      schwarze  691:
                    692:        switch (n->type) {
1.100     schwarze  693:        case MAN_HEAD:
1.11      schwarze  694:                p->flags |= TERMP_NOBREAK;
1.93      schwarze  695:                p->trailspace = 1;
1.11      schwarze  696:                break;
1.100     schwarze  697:        case MAN_BODY:
1.11      schwarze  698:                p->flags |= TERMP_NOSPACE;
                    699:                break;
1.100     schwarze  700:        case MAN_BLOCK:
1.88      schwarze  701:                print_bvspace(p, n, mt->pardist);
1.11      schwarze  702:                /* FALLTHROUGH */
                    703:        default:
                    704:                return(1);
                    705:        }
                    706:
1.70      schwarze  707:        len = (size_t)mt->lmargin[mt->lmargincur];
1.11      schwarze  708:        ival = -1;
                    709:
                    710:        /* Calculate offset. */
1.1       kristaps  711:
1.70      schwarze  712:        if (NULL != (nn = n->parent->head->child))
1.95      schwarze  713:                if (nn->string && 0 == (MAN_LINE & nn->flags))
1.42      schwarze  714:                        if ((ival = a2width(p, nn->string)) >= 0)
1.11      schwarze  715:                                len = (size_t)ival;
1.8       schwarze  716:
1.11      schwarze  717:        switch (n->type) {
1.100     schwarze  718:        case MAN_HEAD:
1.11      schwarze  719:                /* Handle zero-length properly. */
                    720:                if (0 == len)
1.42      schwarze  721:                        len = term_len(p, 1);
1.11      schwarze  722:
1.13      schwarze  723:                p->offset = mt->offset;
                    724:                p->rmargin = mt->offset + len;
1.11      schwarze  725:
1.57      schwarze  726:                savelit = MANT_LITERAL & mt->fl;
                    727:                mt->fl &= ~MANT_LITERAL;
                    728:
1.11      schwarze  729:                /* Don't print same-line elements. */
1.95      schwarze  730:                nn = n->child;
                    731:                while (NULL != nn && 0 == (MAN_LINE & nn->flags))
                    732:                        nn = nn->next;
                    733:
                    734:                while (NULL != nn) {
                    735:                        print_man_node(p, mt, nn, meta);
                    736:                        nn = nn->next;
                    737:                }
1.11      schwarze  738:
1.57      schwarze  739:                if (savelit)
                    740:                        mt->fl |= MANT_LITERAL;
1.11      schwarze  741:                if (ival >= 0)
1.70      schwarze  742:                        mt->lmargin[mt->lmargincur] = (size_t)ival;
1.11      schwarze  743:
                    744:                return(0);
1.100     schwarze  745:        case MAN_BODY:
1.13      schwarze  746:                p->offset = mt->offset + len;
1.94      schwarze  747:                p->rmargin = p->maxrmargin > p->offset ?
1.100     schwarze  748:                    p->maxrmargin : p->offset;
1.93      schwarze  749:                p->trailspace = 0;
1.84      schwarze  750:                p->flags &= ~TERMP_NOBREAK;
1.11      schwarze  751:                break;
                    752:        default:
                    753:                break;
                    754:        }
1.1       kristaps  755:
1.11      schwarze  756:        return(1);
                    757: }
1.1       kristaps  758:
1.11      schwarze  759: static void
                    760: post_TP(DECL_ARGS)
                    761: {
1.1       kristaps  762:
1.11      schwarze  763:        switch (n->type) {
1.100     schwarze  764:        case MAN_HEAD:
1.11      schwarze  765:                term_flushln(p);
                    766:                break;
1.100     schwarze  767:        case MAN_BODY:
1.57      schwarze  768:                term_newln(p);
1.92      schwarze  769:                p->offset = mt->offset;
1.11      schwarze  770:                break;
                    771:        default:
                    772:                break;
                    773:        }
1.1       kristaps  774: }
                    775:
                    776: static int
                    777: pre_SS(DECL_ARGS)
                    778: {
1.88      schwarze  779:        int      i;
1.1       kristaps  780:
1.11      schwarze  781:        switch (n->type) {
1.100     schwarze  782:        case MAN_BLOCK:
1.69      schwarze  783:                mt->fl &= ~MANT_LITERAL;
1.77      schwarze  784:                mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
                    785:                mt->offset = term_len(p, p->defindent);
1.11      schwarze  786:                /* If following a prior empty `SS', no vspace. */
                    787:                if (n->prev && MAN_SS == n->prev->tok)
                    788:                        if (NULL == n->prev->body->child)
                    789:                                break;
                    790:                if (NULL == n->prev)
                    791:                        break;
1.88      schwarze  792:                for (i = 0; i < mt->pardist; i++)
                    793:                        term_vspace(p);
1.11      schwarze  794:                break;
1.100     schwarze  795:        case MAN_HEAD:
1.21      schwarze  796:                term_fontrepl(p, TERMFONT_BOLD);
1.87      schwarze  797:                p->offset = term_len(p, 3);
1.11      schwarze  798:                break;
1.100     schwarze  799:        case MAN_BODY:
1.13      schwarze  800:                p->offset = mt->offset;
1.11      schwarze  801:                break;
                    802:        default:
                    803:                break;
                    804:        }
                    805:
1.1       kristaps  806:        return(1);
                    807: }
                    808:
                    809: static void
                    810: post_SS(DECL_ARGS)
                    811: {
1.100     schwarze  812:
1.11      schwarze  813:        switch (n->type) {
1.100     schwarze  814:        case MAN_HEAD:
1.11      schwarze  815:                term_newln(p);
                    816:                break;
1.100     schwarze  817:        case MAN_BODY:
1.11      schwarze  818:                term_newln(p);
                    819:                break;
                    820:        default:
                    821:                break;
                    822:        }
1.1       kristaps  823: }
                    824:
                    825: static int
                    826: pre_SH(DECL_ARGS)
                    827: {
1.88      schwarze  828:        int      i;
1.1       kristaps  829:
1.11      schwarze  830:        switch (n->type) {
1.100     schwarze  831:        case MAN_BLOCK:
1.69      schwarze  832:                mt->fl &= ~MANT_LITERAL;
1.77      schwarze  833:                mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
                    834:                mt->offset = term_len(p, p->defindent);
1.11      schwarze  835:                /* If following a prior empty `SH', no vspace. */
                    836:                if (n->prev && MAN_SH == n->prev->tok)
                    837:                        if (NULL == n->prev->body->child)
                    838:                                break;
1.29      schwarze  839:                /* If the first macro, no vspae. */
                    840:                if (NULL == n->prev)
                    841:                        break;
1.88      schwarze  842:                for (i = 0; i < mt->pardist; i++)
                    843:                        term_vspace(p);
1.11      schwarze  844:                break;
1.100     schwarze  845:        case MAN_HEAD:
1.21      schwarze  846:                term_fontrepl(p, TERMFONT_BOLD);
1.11      schwarze  847:                p->offset = 0;
                    848:                break;
1.100     schwarze  849:        case MAN_BODY:
1.13      schwarze  850:                p->offset = mt->offset;
1.11      schwarze  851:                break;
                    852:        default:
                    853:                break;
                    854:        }
                    855:
1.1       kristaps  856:        return(1);
                    857: }
                    858:
                    859: static void
                    860: post_SH(DECL_ARGS)
                    861: {
1.100     schwarze  862:
1.11      schwarze  863:        switch (n->type) {
1.100     schwarze  864:        case MAN_HEAD:
1.11      schwarze  865:                term_newln(p);
                    866:                break;
1.100     schwarze  867:        case MAN_BODY:
1.11      schwarze  868:                term_newln(p);
                    869:                break;
                    870:        default:
1.13      schwarze  871:                break;
                    872:        }
                    873: }
                    874:
                    875: static int
                    876: pre_RS(DECL_ARGS)
                    877: {
1.69      schwarze  878:        int              ival;
                    879:        size_t           sz;
1.13      schwarze  880:
                    881:        switch (n->type) {
1.100     schwarze  882:        case MAN_BLOCK:
1.13      schwarze  883:                term_newln(p);
                    884:                return(1);
1.100     schwarze  885:        case MAN_HEAD:
1.13      schwarze  886:                return(0);
                    887:        default:
                    888:                break;
                    889:        }
                    890:
1.77      schwarze  891:        sz = term_len(p, p->defindent);
1.13      schwarze  892:
1.69      schwarze  893:        if (NULL != (n = n->parent->head->child))
1.100     schwarze  894:                if ((ival = a2width(p, n->string)) >= 0)
1.69      schwarze  895:                        sz = (size_t)ival;
1.13      schwarze  896:
1.69      schwarze  897:        mt->offset += sz;
1.94      schwarze  898:        p->offset = mt->offset;
                    899:        p->rmargin = p->maxrmargin > p->offset ?
1.100     schwarze  900:            p->maxrmargin : p->offset;
1.13      schwarze  901:
1.70      schwarze  902:        if (++mt->lmarginsz < MAXMARGINS)
                    903:                mt->lmargincur = mt->lmarginsz;
                    904:
                    905:        mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
1.13      schwarze  906:        return(1);
                    907: }
                    908:
                    909: static void
                    910: post_RS(DECL_ARGS)
                    911: {
1.69      schwarze  912:        int              ival;
                    913:        size_t           sz;
1.13      schwarze  914:
                    915:        switch (n->type) {
1.100     schwarze  916:        case MAN_BLOCK:
1.69      schwarze  917:                return;
1.100     schwarze  918:        case MAN_HEAD:
1.69      schwarze  919:                return;
1.13      schwarze  920:        default:
                    921:                term_newln(p);
1.11      schwarze  922:                break;
                    923:        }
1.69      schwarze  924:
1.77      schwarze  925:        sz = term_len(p, p->defindent);
1.69      schwarze  926:
1.100     schwarze  927:        if (NULL != (n = n->parent->head->child))
                    928:                if ((ival = a2width(p, n->string)) >= 0)
1.69      schwarze  929:                        sz = (size_t)ival;
                    930:
                    931:        mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
                    932:        p->offset = mt->offset;
1.70      schwarze  933:
                    934:        if (--mt->lmarginsz < MAXMARGINS)
                    935:                mt->lmargincur = mt->lmarginsz;
1.91      schwarze  936: }
                    937:
                    938: static int
                    939: pre_UR(DECL_ARGS)
                    940: {
                    941:
                    942:        return (MAN_HEAD != n->type);
                    943: }
                    944:
                    945: static void
                    946: post_UR(DECL_ARGS)
                    947: {
                    948:
                    949:        if (MAN_BLOCK != n->type)
                    950:                return;
                    951:
                    952:        term_word(p, "<");
                    953:        p->flags |= TERMP_NOSPACE;
                    954:
                    955:        if (NULL != n->child->child)
                    956:                print_man_node(p, mt, n->child->child, meta);
                    957:
                    958:        p->flags |= TERMP_NOSPACE;
                    959:        term_word(p, ">");
1.47      schwarze  960: }
                    961:
1.1       kristaps  962: static void
1.19      schwarze  963: print_man_node(DECL_ARGS)
1.1       kristaps  964: {
1.32      schwarze  965:        size_t           rm, rmax;
1.21      schwarze  966:        int              c;
1.1       kristaps  967:
                    968:        switch (n->type) {
1.100     schwarze  969:        case MAN_TEXT:
1.61      schwarze  970:                /*
                    971:                 * If we have a blank line, output a vertical space.
                    972:                 * If we have a space as the first character, break
                    973:                 * before printing the line's data.
                    974:                 */
1.60      schwarze  975:                if ('\0' == *n->string) {
1.1       kristaps  976:                        term_vspace(p);
1.61      schwarze  977:                        return;
                    978:                } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60      schwarze  979:                        term_newln(p);
1.21      schwarze  980:
1.1       kristaps  981:                term_word(p, n->string);
1.84      schwarze  982:                goto out;
1.21      schwarze  983:
1.100     schwarze  984:        case MAN_EQN:
1.105     schwarze  985:                if ( ! (n->flags & MAN_LINE))
                    986:                        p->flags |= TERMP_NOSPACE;
1.71      schwarze  987:                term_eqn(p, n->eqn);
1.107     schwarze  988:                if (n->next != NULL && ! (n->next->flags & MAN_LINE))
1.106     schwarze  989:                        p->flags |= TERMP_NOSPACE;
1.61      schwarze  990:                return;
1.100     schwarze  991:        case MAN_TBL:
1.61      schwarze  992:                /*
                    993:                 * Tables are preceded by a newline.  Then process a
                    994:                 * table line, which will cause line termination,
                    995:                 */
1.100     schwarze  996:                if (TBL_SPAN_FIRST & n->span->flags)
1.58      schwarze  997:                        term_newln(p);
                    998:                term_tbl(p, n->span);
1.61      schwarze  999:                return;
1.1       kristaps 1000:        default:
                   1001:                break;
                   1002:        }
                   1003:
1.61      schwarze 1004:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                   1005:                term_fontrepl(p, TERMFONT_NONE);
                   1006:
                   1007:        c = 1;
                   1008:        if (termacts[n->tok].pre)
1.89      schwarze 1009:                c = (*termacts[n->tok].pre)(p, mt, n, meta);
1.61      schwarze 1010:
1.1       kristaps 1011:        if (c && n->child)
1.89      schwarze 1012:                print_man_nodelist(p, mt, n->child, meta);
1.1       kristaps 1013:
1.61      schwarze 1014:        if (termacts[n->tok].post)
1.89      schwarze 1015:                (*termacts[n->tok].post)(p, mt, n, meta);
1.61      schwarze 1016:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                   1017:                term_fontrepl(p, TERMFONT_NONE);
1.30      schwarze 1018:
1.84      schwarze 1019: out:
                   1020:        /*
                   1021:         * If we're in a literal context, make sure that words
                   1022:         * together on the same line stay together.  This is a
                   1023:         * POST-printing call, so we check the NEXT word.  Since
                   1024:         * -man doesn't have nested macros, we don't need to be
                   1025:         * more specific than this.
                   1026:         */
                   1027:        if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1.96      schwarze 1028:            (NULL == n->next || MAN_LINE & n->next->flags)) {
1.84      schwarze 1029:                rm = p->rmargin;
                   1030:                rmax = p->maxrmargin;
                   1031:                p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
                   1032:                p->flags |= TERMP_NOSPACE;
                   1033:                if (NULL != n->string && '\0' != *n->string)
                   1034:                        term_flushln(p);
                   1035:                else
                   1036:                        term_newln(p);
                   1037:                if (rm < rmax && n->parent->tok == MAN_HP) {
                   1038:                        p->offset = rm;
                   1039:                        p->rmargin = rmax;
                   1040:                } else
                   1041:                        p->rmargin = rm;
                   1042:                p->maxrmargin = rmax;
                   1043:        }
1.30      schwarze 1044:        if (MAN_EOS & n->flags)
                   1045:                p->flags |= TERMP_SENTENCE;
1.1       kristaps 1046: }
                   1047:
                   1048:
                   1049: static void
1.21      schwarze 1050: print_man_nodelist(DECL_ARGS)
1.1       kristaps 1051: {
1.11      schwarze 1052:
1.89      schwarze 1053:        print_man_node(p, mt, n, meta);
1.1       kristaps 1054:        if ( ! n->next)
                   1055:                return;
1.89      schwarze 1056:        print_man_nodelist(p, mt, n->next, meta);
1.1       kristaps 1057: }
                   1058:
                   1059: static void
1.41      schwarze 1060: print_man_foot(struct termp *p, const void *arg)
1.1       kristaps 1061: {
1.101     schwarze 1062:        const struct man_meta   *meta;
                   1063:        char                    *title;
                   1064:        size_t                   datelen;
1.41      schwarze 1065:
                   1066:        meta = (const struct man_meta *)arg;
1.80      schwarze 1067:        assert(meta->title);
                   1068:        assert(meta->msec);
                   1069:        assert(meta->date);
1.21      schwarze 1070:
                   1071:        term_fontrepl(p, TERMFONT_NONE);
1.1       kristaps 1072:
1.103     schwarze 1073:        if (meta->hasbody)
                   1074:                term_vspace(p);
1.81      schwarze 1075:
                   1076:        /*
                   1077:         * Temporary, undocumented option to imitate mdoc(7) output.
                   1078:         * In the bottom right corner, use the source instead of
                   1079:         * the title.
                   1080:         */
                   1081:
1.79      schwarze 1082:        if ( ! p->mdocstyle) {
1.103     schwarze 1083:                if (meta->hasbody) {
                   1084:                        term_vspace(p);
                   1085:                        term_vspace(p);
                   1086:                }
1.101     schwarze 1087:                mandoc_asprintf(&title, "%s(%s)",
                   1088:                    meta->title, meta->msec);
1.79      schwarze 1089:        } else if (meta->source) {
1.101     schwarze 1090:                title = mandoc_strdup(meta->source);
1.79      schwarze 1091:        } else {
1.101     schwarze 1092:                title = mandoc_strdup("");
1.79      schwarze 1093:        }
1.78      schwarze 1094:        datelen = term_strlen(p, meta->date);
1.1       kristaps 1095:
1.81      schwarze 1096:        /* Bottom left corner: manual source. */
                   1097:
1.1       kristaps 1098:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.93      schwarze 1099:        p->trailspace = 1;
1.1       kristaps 1100:        p->offset = 0;
1.78      schwarze 1101:        p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1.1       kristaps 1102:
                   1103:        if (meta->source)
                   1104:                term_word(p, meta->source);
                   1105:        term_flushln(p);
                   1106:
1.81      schwarze 1107:        /* At the bottom in the middle: manual date. */
                   1108:
1.72      schwarze 1109:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1110:        p->offset = p->rmargin;
1.78      schwarze 1111:        p->rmargin = p->maxrmargin - term_strlen(p, title);
                   1112:        if (p->offset + datelen >= p->rmargin)
                   1113:                p->rmargin = p->offset + datelen;
                   1114:
                   1115:        term_word(p, meta->date);
                   1116:        term_flushln(p);
                   1117:
1.81      schwarze 1118:        /* Bottom right corner: manual title and section. */
                   1119:
1.78      schwarze 1120:        p->flags &= ~TERMP_NOBREAK;
                   1121:        p->flags |= TERMP_NOSPACE;
1.93      schwarze 1122:        p->trailspace = 0;
1.78      schwarze 1123:        p->offset = p->rmargin;
1.1       kristaps 1124:        p->rmargin = p->maxrmargin;
                   1125:
1.78      schwarze 1126:        term_word(p, title);
1.1       kristaps 1127:        term_flushln(p);
1.101     schwarze 1128:        free(title);
1.1       kristaps 1129: }
                   1130:
                   1131: static void
1.41      schwarze 1132: print_man_head(struct termp *p, const void *arg)
1.1       kristaps 1133: {
1.101     schwarze 1134:        const struct man_meta   *meta;
1.102     schwarze 1135:        const char              *volume;
1.101     schwarze 1136:        char                    *title;
1.102     schwarze 1137:        size_t                   vollen, titlen;
1.41      schwarze 1138:
1.89      schwarze 1139:        meta = (const struct man_meta *)arg;
                   1140:        assert(meta->title);
                   1141:        assert(meta->msec);
1.1       kristaps 1142:
1.102     schwarze 1143:        volume = NULL == meta->vol ? "" : meta->vol;
                   1144:        vollen = term_strlen(p, volume);
1.1       kristaps 1145:
1.81      schwarze 1146:        /* Top left corner: manual title and section. */
                   1147:
1.101     schwarze 1148:        mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1.42      schwarze 1149:        titlen = term_strlen(p, title);
1.1       kristaps 1150:
1.73      schwarze 1151:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.93      schwarze 1152:        p->trailspace = 1;
1.1       kristaps 1153:        p->offset = 0;
1.102     schwarze 1154:        p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
                   1155:            (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
                   1156:            p->maxrmargin - vollen;
1.1       kristaps 1157:
                   1158:        term_word(p, title);
                   1159:        term_flushln(p);
                   1160:
1.81      schwarze 1161:        /* At the top in the middle: manual volume. */
                   1162:
1.72      schwarze 1163:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1164:        p->offset = p->rmargin;
1.102     schwarze 1165:        p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1.25      schwarze 1166:            p->maxrmargin - titlen : p->maxrmargin;
1.1       kristaps 1167:
1.102     schwarze 1168:        term_word(p, volume);
1.1       kristaps 1169:        term_flushln(p);
                   1170:
1.81      schwarze 1171:        /* Top right corner: title and section, again. */
                   1172:
1.1       kristaps 1173:        p->flags &= ~TERMP_NOBREAK;
1.93      schwarze 1174:        p->trailspace = 0;
1.25      schwarze 1175:        if (p->rmargin + titlen <= p->maxrmargin) {
1.72      schwarze 1176:                p->flags |= TERMP_NOSPACE;
1.25      schwarze 1177:                p->offset = p->rmargin;
                   1178:                p->rmargin = p->maxrmargin;
                   1179:                term_word(p, title);
                   1180:                term_flushln(p);
                   1181:        }
1.1       kristaps 1182:
1.73      schwarze 1183:        p->flags &= ~TERMP_NOSPACE;
                   1184:        p->offset = 0;
1.1       kristaps 1185:        p->rmargin = p->maxrmargin;
1.29      schwarze 1186:
1.100     schwarze 1187:        /*
1.81      schwarze 1188:         * Groff prints three blank lines before the content.
                   1189:         * Do the same, except in the temporary, undocumented
                   1190:         * mode imitating mdoc(7) output.
1.29      schwarze 1191:         */
                   1192:
                   1193:        term_vspace(p);
1.79      schwarze 1194:        if ( ! p->mdocstyle) {
                   1195:                term_vspace(p);
                   1196:                term_vspace(p);
                   1197:        }
1.101     schwarze 1198:        free(title);
1.1       kristaps 1199: }