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

Annotation of mandoc/man_term.c, Revision 1.139

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

CVSweb