[BACK]Return to man_term.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/man_term.c, Revision 1.76

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