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

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