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

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