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

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