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

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