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

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