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

Annotation of mandoc/man_term.c, Revision 1.213

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

CVSweb