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

1.68    ! schwarze    1: /*     $Id: man_term.c,v 1.67 2011/04/24 16:22:02 schwarze Exp $ */
1.1       kristaps    2: /*
1.58      schwarze    3:  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.57      schwarze    4:  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1       kristaps    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
1.2       schwarze    7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    9:  *
1.2       schwarze   10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       kristaps   17:  */
1.13      schwarze   18: #include <sys/types.h>
                     19:
1.1       kristaps   20: #include <assert.h>
1.11      schwarze   21: #include <ctype.h>
1.1       kristaps   22: #include <stdio.h>
                     23: #include <stdlib.h>
                     24: #include <string.h>
                     25:
1.37      schwarze   26: #include "mandoc.h"
1.18      schwarze   27: #include "out.h"
                     28: #include "man.h"
1.1       kristaps   29: #include "term.h"
1.18      schwarze   30: #include "main.h"
1.10      schwarze   31:
                     32: #define        INDENT            7
                     33: #define        HALFINDENT        3
1.1       kristaps   34:
1.19      schwarze   35: /* FIXME: have PD set the default vspace width. */
                     36:
1.11      schwarze   37: struct mtermp {
                     38:        int               fl;
                     39: #define        MANT_LITERAL     (1 << 0)
1.13      schwarze   40:        /*
                     41:         * Default amount to indent the left margin after leading text
                     42:         * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
                     43:         * indent).  This needs to be saved because `HP' and so on, if
                     44:         * not having a specified value, must default.
                     45:         *
                     46:         * Note that this is the indentation AFTER the left offset, so
                     47:         * the total offset is usually offset + lmargin.
                     48:         */
                     49:        size_t            lmargin;
                     50:        /*
                     51:         * The default offset, i.e., the amount between any text and the
                     52:         * page boundary.
                     53:         */
                     54:        size_t            offset;
1.11      schwarze   55: };
                     56:
1.1       kristaps   57: #define        DECL_ARGS         struct termp *p, \
1.11      schwarze   58:                          struct mtermp *mt, \
1.1       kristaps   59:                          const struct man_node *n, \
                     60:                          const struct man_meta *m
                     61:
                     62: struct termact {
                     63:        int             (*pre)(DECL_ARGS);
                     64:        void            (*post)(DECL_ARGS);
1.26      schwarze   65:        int               flags;
                     66: #define        MAN_NOTEXT       (1 << 0) /* Never has text children. */
1.1       kristaps   67: };
                     68:
1.42      schwarze   69: static int               a2width(const struct termp *, const char *);
                     70: static size_t            a2height(const struct termp *, const char *);
1.18      schwarze   71:
1.21      schwarze   72: static void              print_man_nodelist(DECL_ARGS);
1.19      schwarze   73: static void              print_man_node(DECL_ARGS);
1.41      schwarze   74: static void              print_man_head(struct termp *, const void *);
                     75: static void              print_man_foot(struct termp *, const void *);
1.18      schwarze   76: static void              print_bvspace(struct termp *,
                     77:                                const struct man_node *);
                     78:
1.51      schwarze   79: static int               pre_alternate(DECL_ARGS);
1.1       kristaps   80: static int               pre_B(DECL_ARGS);
1.11      schwarze   81: static int               pre_HP(DECL_ARGS);
1.1       kristaps   82: static int               pre_I(DECL_ARGS);
                     83: static int               pre_IP(DECL_ARGS);
                     84: static int               pre_PP(DECL_ARGS);
1.13      schwarze   85: static int               pre_RS(DECL_ARGS);
1.1       kristaps   86: static int               pre_SH(DECL_ARGS);
                     87: static int               pre_SS(DECL_ARGS);
                     88: static int               pre_TP(DECL_ARGS);
1.14      schwarze   89: static int               pre_ign(DECL_ARGS);
1.45      schwarze   90: static int               pre_in(DECL_ARGS);
                     91: static int               pre_literal(DECL_ARGS);
1.11      schwarze   92: static int               pre_sp(DECL_ARGS);
1.52      schwarze   93: static int               pre_ft(DECL_ARGS);
1.1       kristaps   94:
1.11      schwarze   95: static void              post_IP(DECL_ARGS);
                     96: static void              post_HP(DECL_ARGS);
1.13      schwarze   97: static void              post_RS(DECL_ARGS);
1.1       kristaps   98: static void              post_SH(DECL_ARGS);
                     99: static void              post_SS(DECL_ARGS);
1.11      schwarze  100: static void              post_TP(DECL_ARGS);
1.1       kristaps  101:
1.17      schwarze  102: static const struct termact termacts[MAN_MAX] = {
1.45      schwarze  103:        { pre_sp, NULL, MAN_NOTEXT }, /* br */
1.26      schwarze  104:        { NULL, NULL, 0 }, /* TH */
                    105:        { pre_SH, post_SH, 0 }, /* SH */
                    106:        { pre_SS, post_SS, 0 }, /* SS */
                    107:        { pre_TP, post_TP, 0 }, /* TP */
                    108:        { pre_PP, NULL, 0 }, /* LP */
                    109:        { pre_PP, NULL, 0 }, /* PP */
                    110:        { pre_PP, NULL, 0 }, /* P */
                    111:        { pre_IP, post_IP, 0 }, /* IP */
                    112:        { pre_HP, post_HP, 0 }, /* HP */
                    113:        { NULL, NULL, 0 }, /* SM */
                    114:        { pre_B, NULL, 0 }, /* SB */
1.51      schwarze  115:        { pre_alternate, NULL, 0 }, /* BI */
                    116:        { pre_alternate, NULL, 0 }, /* IB */
                    117:        { pre_alternate, NULL, 0 }, /* BR */
                    118:        { pre_alternate, NULL, 0 }, /* RB */
1.26      schwarze  119:        { NULL, NULL, 0 }, /* R */
                    120:        { pre_B, NULL, 0 }, /* B */
                    121:        { pre_I, NULL, 0 }, /* I */
1.51      schwarze  122:        { pre_alternate, NULL, 0 }, /* IR */
                    123:        { pre_alternate, NULL, 0 }, /* RI */
1.62      schwarze  124:        { pre_ign, NULL, MAN_NOTEXT }, /* na */
1.26      schwarze  125:        { pre_sp, NULL, MAN_NOTEXT }, /* sp */
1.45      schwarze  126:        { pre_literal, NULL, 0 }, /* nf */
                    127:        { pre_literal, NULL, 0 }, /* fi */
1.26      schwarze  128:        { NULL, NULL, 0 }, /* RE */
                    129:        { pre_RS, post_RS, 0 }, /* RS */
                    130:        { pre_ign, NULL, 0 }, /* DT */
                    131:        { pre_ign, NULL, 0 }, /* UC */
                    132:        { pre_ign, NULL, 0 }, /* PD */
1.36      schwarze  133:        { pre_ign, NULL, 0 }, /* AT */
1.45      schwarze  134:        { pre_in, NULL, MAN_NOTEXT }, /* in */
1.52      schwarze  135:        { pre_ft, NULL, MAN_NOTEXT }, /* ft */
1.1       kristaps  136: };
1.11      schwarze  137:
1.1       kristaps  138:
                    139:
1.16      schwarze  140: void
1.18      schwarze  141: terminal_man(void *arg, const struct man *man)
1.1       kristaps  142: {
1.18      schwarze  143:        struct termp            *p;
                    144:        const struct man_node   *n;
                    145:        const struct man_meta   *m;
                    146:        struct mtermp            mt;
                    147:
                    148:        p = (struct termp *)arg;
                    149:
1.39      schwarze  150:        p->overstep = 0;
1.32      schwarze  151:        p->maxrmargin = p->defrmargin;
1.42      schwarze  152:        p->tabwidth = term_len(p, 5);
1.27      schwarze  153:
1.18      schwarze  154:        if (NULL == p->symtab)
1.68    ! schwarze  155:                p->symtab = mchars_alloc();
1.18      schwarze  156:
                    157:        n = man_node(man);
                    158:        m = man_meta(man);
1.1       kristaps  159:
1.41      schwarze  160:        term_begin(p, print_man_head, print_man_foot, m);
1.1       kristaps  161:        p->flags |= TERMP_NOSPACE;
1.11      schwarze  162:
                    163:        mt.fl = 0;
1.42      schwarze  164:        mt.lmargin = term_len(p, INDENT);
                    165:        mt.offset = term_len(p, INDENT);
1.11      schwarze  166:
1.18      schwarze  167:        if (n->child)
1.21      schwarze  168:                print_man_nodelist(p, &mt, n->child, m);
1.41      schwarze  169:
                    170:        term_end(p);
1.1       kristaps  171: }
                    172:
                    173:
1.42      schwarze  174: static size_t
                    175: a2height(const struct termp *p, const char *cp)
1.11      schwarze  176: {
1.18      schwarze  177:        struct roffsu    su;
1.11      schwarze  178:
1.42      schwarze  179:        if ( ! a2roffsu(cp, &su, SCALE_VS))
                    180:                SCALE_VS_INIT(&su, term_strlen(p, cp));
1.11      schwarze  181:
1.42      schwarze  182:        return(term_vspan(p, &su));
1.11      schwarze  183: }
                    184:
                    185:
                    186: static int
1.42      schwarze  187: a2width(const struct termp *p, const char *cp)
1.11      schwarze  188: {
1.18      schwarze  189:        struct roffsu    su;
1.11      schwarze  190:
1.42      schwarze  191:        if ( ! a2roffsu(cp, &su, SCALE_BU))
1.18      schwarze  192:                return(-1);
                    193:
1.42      schwarze  194:        return((int)term_hspan(p, &su));
1.18      schwarze  195: }
1.11      schwarze  196:
                    197:
1.18      schwarze  198: static void
                    199: print_bvspace(struct termp *p, const struct man_node *n)
                    200: {
                    201:        term_newln(p);
1.64      schwarze  202:
                    203:        if (n->body && n->body->child && MAN_TBL == n->body->child->type)
                    204:                return;
1.11      schwarze  205:
1.18      schwarze  206:        if (NULL == n->prev)
                    207:                return;
1.11      schwarze  208:
1.18      schwarze  209:        if (MAN_SS == n->prev->tok)
                    210:                return;
                    211:        if (MAN_SH == n->prev->tok)
                    212:                return;
1.11      schwarze  213:
1.18      schwarze  214:        term_vspace(p);
1.14      schwarze  215: }
                    216:
                    217:
                    218: /* ARGSUSED */
                    219: static int
                    220: pre_ign(DECL_ARGS)
                    221: {
                    222:
                    223:        return(0);
1.11      schwarze  224: }
                    225:
                    226:
1.1       kristaps  227: /* ARGSUSED */
                    228: static int
                    229: pre_I(DECL_ARGS)
                    230: {
                    231:
1.21      schwarze  232:        term_fontrepl(p, TERMFONT_UNDER);
1.1       kristaps  233:        return(1);
                    234: }
                    235:
                    236:
                    237: /* ARGSUSED */
1.11      schwarze  238: static int
1.45      schwarze  239: pre_literal(DECL_ARGS)
1.11      schwarze  240: {
                    241:
1.45      schwarze  242:        term_newln(p);
1.53      schwarze  243:
                    244:        if (MAN_nf == n->tok)
1.45      schwarze  245:                mt->fl |= MANT_LITERAL;
1.53      schwarze  246:        else
1.45      schwarze  247:                mt->fl &= ~MANT_LITERAL;
                    248:
1.62      schwarze  249:        return(0);
1.11      schwarze  250: }
                    251:
                    252: /* ARGSUSED */
                    253: static int
1.51      schwarze  254: pre_alternate(DECL_ARGS)
1.1       kristaps  255: {
1.51      schwarze  256:        enum termfont            font[2];
                    257:        const struct man_node   *nn;
                    258:        int                      savelit, i;
1.1       kristaps  259:
1.51      schwarze  260:        switch (n->tok) {
                    261:        case (MAN_RB):
                    262:                font[0] = TERMFONT_NONE;
                    263:                font[1] = TERMFONT_BOLD;
                    264:                break;
                    265:        case (MAN_RI):
                    266:                font[0] = TERMFONT_NONE;
                    267:                font[1] = TERMFONT_UNDER;
                    268:                break;
                    269:        case (MAN_BR):
                    270:                font[0] = TERMFONT_BOLD;
                    271:                font[1] = TERMFONT_NONE;
                    272:                break;
                    273:        case (MAN_BI):
                    274:                font[0] = TERMFONT_BOLD;
                    275:                font[1] = TERMFONT_UNDER;
                    276:                break;
                    277:        case (MAN_IR):
                    278:                font[0] = TERMFONT_UNDER;
                    279:                font[1] = TERMFONT_NONE;
                    280:                break;
                    281:        case (MAN_IB):
                    282:                font[0] = TERMFONT_UNDER;
                    283:                font[1] = TERMFONT_BOLD;
                    284:                break;
                    285:        default:
                    286:                abort();
                    287:        }
1.17      schwarze  288:
1.51      schwarze  289:        savelit = MANT_LITERAL & mt->fl;
                    290:        mt->fl &= ~MANT_LITERAL;
1.17      schwarze  291:
1.51      schwarze  292:        for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
                    293:                term_fontrepl(p, font[i]);
                    294:                if (savelit && NULL == nn->next)
                    295:                        mt->fl |= MANT_LITERAL;
1.19      schwarze  296:                print_man_node(p, mt, nn, m);
1.51      schwarze  297:                if (nn->next)
1.1       kristaps  298:                        p->flags |= TERMP_NOSPACE;
                    299:        }
1.17      schwarze  300:
1.1       kristaps  301:        return(0);
                    302: }
                    303:
                    304: /* ARGSUSED */
                    305: static int
                    306: pre_B(DECL_ARGS)
                    307: {
                    308:
1.21      schwarze  309:        term_fontrepl(p, TERMFONT_BOLD);
1.1       kristaps  310:        return(1);
1.52      schwarze  311: }
                    312:
                    313: /* ARGSUSED */
                    314: static int
                    315: pre_ft(DECL_ARGS)
                    316: {
                    317:        const char      *cp;
                    318:
                    319:        if (NULL == n->child) {
                    320:                term_fontlast(p);
                    321:                return(0);
                    322:        }
                    323:
                    324:        cp = n->child->string;
                    325:        switch (*cp) {
                    326:        case ('4'):
                    327:                /* FALLTHROUGH */
                    328:        case ('3'):
                    329:                /* FALLTHROUGH */
                    330:        case ('B'):
                    331:                term_fontrepl(p, TERMFONT_BOLD);
                    332:                break;
                    333:        case ('2'):
                    334:                /* FALLTHROUGH */
                    335:        case ('I'):
                    336:                term_fontrepl(p, TERMFONT_UNDER);
                    337:                break;
                    338:        case ('P'):
                    339:                term_fontlast(p);
                    340:                break;
                    341:        case ('1'):
                    342:                /* FALLTHROUGH */
                    343:        case ('C'):
                    344:                /* FALLTHROUGH */
                    345:        case ('R'):
                    346:                term_fontrepl(p, TERMFONT_NONE);
                    347:                break;
                    348:        default:
                    349:                break;
                    350:        }
                    351:        return(0);
1.1       kristaps  352: }
                    353:
                    354: /* ARGSUSED */
                    355: static int
1.45      schwarze  356: pre_in(DECL_ARGS)
1.11      schwarze  357: {
1.45      schwarze  358:        int              len, less;
                    359:        size_t           v;
                    360:        const char      *cp;
                    361:
                    362:        term_newln(p);
                    363:
                    364:        if (NULL == n->child) {
                    365:                p->offset = mt->offset;
                    366:                return(0);
                    367:        }
1.11      schwarze  368:
1.45      schwarze  369:        cp = n->child->string;
                    370:        less = 0;
1.11      schwarze  371:
1.45      schwarze  372:        if ('-' == *cp)
                    373:                less = -1;
                    374:        else if ('+' == *cp)
                    375:                less = 1;
                    376:        else
                    377:                cp--;
                    378:
                    379:        if ((len = a2width(p, ++cp)) < 0)
                    380:                return(0);
                    381:
                    382:        v = (size_t)len;
                    383:
                    384:        if (less < 0)
                    385:                p->offset -= p->offset > v ? v : p->offset;
                    386:        else if (less > 0)
                    387:                p->offset += v;
                    388:        else
                    389:                p->offset = v;
1.59      schwarze  390:
                    391:        /* Don't let this creep beyond the right margin. */
                    392:
                    393:        if (p->offset > p->rmargin)
                    394:                p->offset = p->rmargin;
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:        switch (n->type) {
                    850:        case(MAN_TEXT):
1.61      schwarze  851:                /*
                    852:                 * If we have a blank line, output a vertical space.
                    853:                 * If we have a space as the first character, break
                    854:                 * before printing the line's data.
                    855:                 */
1.60      schwarze  856:                if ('\0' == *n->string) {
1.1       kristaps  857:                        term_vspace(p);
1.61      schwarze  858:                        return;
                    859:                } else if (' ' == *n->string && MAN_LINE & n->flags)
1.60      schwarze  860:                        term_newln(p);
1.21      schwarze  861:
1.1       kristaps  862:                term_word(p, n->string);
1.21      schwarze  863:
1.61      schwarze  864:                /*
                    865:                 * If we're in a literal context, make sure that words
                    866:                 * togehter on the same line stay together.  This is a
                    867:                 * POST-printing call, so we check the NEXT word.  Since
                    868:                 * -man doesn't have nested macros, we don't need to be
                    869:                 * more specific than this.
                    870:                 */
                    871:                if (MANT_LITERAL & mt->fl &&
                    872:                                (NULL == n->next ||
                    873:                                 n->next->line > n->line)) {
1.32      schwarze  874:                        rm = p->rmargin;
                    875:                        rmax = p->maxrmargin;
1.29      schwarze  876:                        p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1.11      schwarze  877:                        p->flags |= TERMP_NOSPACE;
                    878:                        term_flushln(p);
1.57      schwarze  879:                        p->flags &= ~TERMP_NOLPAD;
1.32      schwarze  880:                        p->rmargin = rm;
                    881:                        p->maxrmargin = rmax;
1.11      schwarze  882:                }
1.63      schwarze  883:
                    884:                if (MAN_EOS & n->flags)
                    885:                        p->flags |= TERMP_SENTENCE;
1.66      schwarze  886:                return;
                    887:        case (MAN_EQN):
                    888:                term_word(p, n->eqn->data);
1.61      schwarze  889:                return;
1.58      schwarze  890:        case (MAN_TBL):
1.61      schwarze  891:                /*
                    892:                 * Tables are preceded by a newline.  Then process a
                    893:                 * table line, which will cause line termination,
                    894:                 */
1.58      schwarze  895:                if (TBL_SPAN_FIRST & n->span->flags)
                    896:                        term_newln(p);
                    897:                term_tbl(p, n->span);
1.61      schwarze  898:                return;
1.1       kristaps  899:        default:
                    900:                break;
                    901:        }
                    902:
1.61      schwarze  903:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                    904:                term_fontrepl(p, TERMFONT_NONE);
                    905:
                    906:        c = 1;
                    907:        if (termacts[n->tok].pre)
                    908:                c = (*termacts[n->tok].pre)(p, mt, n, m);
                    909:
1.1       kristaps  910:        if (c && n->child)
1.21      schwarze  911:                print_man_nodelist(p, mt, n->child, m);
1.1       kristaps  912:
1.61      schwarze  913:        if (termacts[n->tok].post)
                    914:                (*termacts[n->tok].post)(p, mt, n, m);
                    915:        if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
                    916:                term_fontrepl(p, TERMFONT_NONE);
1.30      schwarze  917:
                    918:        if (MAN_EOS & n->flags)
                    919:                p->flags |= TERMP_SENTENCE;
1.1       kristaps  920: }
                    921:
                    922:
                    923: static void
1.21      schwarze  924: print_man_nodelist(DECL_ARGS)
1.1       kristaps  925: {
1.11      schwarze  926:
1.19      schwarze  927:        print_man_node(p, mt, n, m);
1.1       kristaps  928:        if ( ! n->next)
                    929:                return;
1.21      schwarze  930:        print_man_nodelist(p, mt, n->next, m);
1.1       kristaps  931: }
                    932:
                    933:
                    934: static void
1.41      schwarze  935: print_man_foot(struct termp *p, const void *arg)
1.1       kristaps  936: {
1.41      schwarze  937:        const struct man_meta *meta;
                    938:
                    939:        meta = (const struct man_meta *)arg;
1.21      schwarze  940:
                    941:        term_fontrepl(p, TERMFONT_NONE);
1.1       kristaps  942:
1.38      schwarze  943:        term_vspace(p);
                    944:        term_vspace(p);
1.1       kristaps  945:        term_vspace(p);
                    946:
                    947:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1.65      schwarze  948:        p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
1.1       kristaps  949:        p->offset = 0;
1.46      schwarze  950:
                    951:        /* term_strlen() can return zero. */
                    952:        if (p->rmargin == p->maxrmargin)
                    953:                p->rmargin--;
1.1       kristaps  954:
                    955:        if (meta->source)
                    956:                term_word(p, meta->source);
                    957:        if (meta->source)
                    958:                term_word(p, "");
                    959:        term_flushln(p);
                    960:
                    961:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    962:        p->offset = p->rmargin;
                    963:        p->rmargin = p->maxrmargin;
                    964:        p->flags &= ~TERMP_NOBREAK;
                    965:
1.65      schwarze  966:        term_word(p, meta->date);
1.1       kristaps  967:        term_flushln(p);
                    968: }
                    969:
                    970:
                    971: static void
1.41      schwarze  972: print_man_head(struct termp *p, const void *arg)
1.1       kristaps  973: {
1.20      schwarze  974:        char            buf[BUFSIZ], title[BUFSIZ];
1.25      schwarze  975:        size_t          buflen, titlen;
1.41      schwarze  976:        const struct man_meta *m;
                    977:
                    978:        m = (const struct man_meta *)arg;
1.1       kristaps  979:
1.29      schwarze  980:        /*
                    981:         * Note that old groff would spit out some spaces before the
                    982:         * header.  We discontinue this strange behaviour, but at one
                    983:         * point we did so here.
                    984:         */
                    985:
1.1       kristaps  986:        p->rmargin = p->maxrmargin;
1.27      schwarze  987:
1.1       kristaps  988:        p->offset = 0;
1.20      schwarze  989:        buf[0] = title[0] = '\0';
1.1       kristaps  990:
1.20      schwarze  991:        if (m->vol)
                    992:                strlcpy(buf, m->vol, BUFSIZ);
1.42      schwarze  993:        buflen = term_strlen(p, buf);
1.1       kristaps  994:
1.31      schwarze  995:        snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1.42      schwarze  996:        titlen = term_strlen(p, title);
1.1       kristaps  997:
                    998:        p->offset = 0;
1.25      schwarze  999:        p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1.42      schwarze 1000:            (p->maxrmargin -
                   1001:             term_strlen(p, buf) + term_len(p, 1)) / 2 :
1.25      schwarze 1002:            p->maxrmargin - buflen;
1.1       kristaps 1003:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                   1004:
                   1005:        term_word(p, title);
                   1006:        term_flushln(p);
                   1007:
                   1008:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                   1009:        p->offset = p->rmargin;
1.25      schwarze 1010:        p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
                   1011:            p->maxrmargin - titlen : p->maxrmargin;
1.1       kristaps 1012:
                   1013:        term_word(p, buf);
                   1014:        term_flushln(p);
                   1015:
                   1016:        p->flags &= ~TERMP_NOBREAK;
1.25      schwarze 1017:        if (p->rmargin + titlen <= p->maxrmargin) {
                   1018:                p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                   1019:                p->offset = p->rmargin;
                   1020:                p->rmargin = p->maxrmargin;
                   1021:                term_word(p, title);
                   1022:                term_flushln(p);
                   1023:        }
1.1       kristaps 1024:
                   1025:        p->rmargin = p->maxrmargin;
                   1026:        p->offset = 0;
                   1027:        p->flags &= ~TERMP_NOSPACE;
1.29      schwarze 1028:
                   1029:        /*
                   1030:         * Groff likes to have some leading spaces before content.  Well
                   1031:         * that's fine by me.
                   1032:         */
                   1033:
                   1034:        term_vspace(p);
                   1035:        term_vspace(p);
                   1036:        term_vspace(p);
1.1       kristaps 1037: }