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

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