[BACK]Return to mdoc_term.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/mdoc_term.c, Revision 1.376

1.376   ! schwarze    1: /*     $Id: mdoc_term.c,v 1.375 2020/01/19 18:02:00 schwarze Exp $ */
1.1       kristaps    2: /*
1.217     schwarze    3:  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.375     schwarze    4:  * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
1.256     schwarze    5:  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
1.1       kristaps    6:  *
                      7:  * Permission to use, copy, modify, and distribute this software for any
1.6       kristaps    8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps   10:  *
1.314     schwarze   11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.6       kristaps   12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.314     schwarze   13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.6       kristaps   14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       kristaps   18:  */
1.107     kristaps   19: #include "config.h"
                     20:
1.1       kristaps   21: #include <sys/types.h>
                     22:
                     23: #include <assert.h>
                     24: #include <ctype.h>
1.302     schwarze   25: #include <limits.h>
1.118     kristaps   26: #include <stdint.h>
1.1       kristaps   27: #include <stdio.h>
                     28: #include <stdlib.h>
                     29: #include <string.h>
                     30:
1.314     schwarze   31: #include "mandoc_aux.h"
                     32: #include "roff.h"
                     33: #include "mdoc.h"
1.92      kristaps   34: #include "out.h"
1.1       kristaps   35: #include "term.h"
1.320     schwarze   36: #include "tag.h"
1.89      kristaps   37: #include "main.h"
1.1       kristaps   38:
                     39: struct termpair {
                     40:        struct termpair  *ppair;
1.31      kristaps   41:        int               count;
1.1       kristaps   42: };
                     43:
1.31      kristaps   44: #define        DECL_ARGS struct termp *p, \
                     45:                  struct termpair *pair, \
1.316     schwarze   46:                  const struct roff_meta *meta, \
1.315     schwarze   47:                  struct roff_node *n
1.1       kristaps   48:
1.368     schwarze   49: struct mdoc_term_act {
1.1       kristaps   50:        int     (*pre)(DECL_ARGS);
                     51:        void    (*post)(DECL_ARGS);
                     52: };
                     53:
1.302     schwarze   54: static int       a2width(const struct termp *, const char *);
1.96      kristaps   55:
                     56: static void      print_bvspace(struct termp *,
1.315     schwarze   57:                        const struct roff_node *,
                     58:                        const struct roff_node *);
1.264     schwarze   59: static void      print_mdoc_node(DECL_ARGS);
1.145     kristaps   60: static void      print_mdoc_nodelist(DECL_ARGS);
1.316     schwarze   61: static void      print_mdoc_head(struct termp *, const struct roff_meta *);
                     62: static void      print_mdoc_foot(struct termp *, const struct roff_meta *);
1.264     schwarze   63: static void      synopsis_pre(struct termp *,
1.315     schwarze   64:                        const struct roff_node *);
1.96      kristaps   65:
1.31      kristaps   66: static void      termp____post(DECL_ARGS);
1.203     kristaps   67: static void      termp__t_post(DECL_ARGS);
1.31      kristaps   68: static void      termp_bd_post(DECL_ARGS);
1.159     schwarze   69: static void      termp_bk_post(DECL_ARGS);
1.31      kristaps   70: static void      termp_bl_post(DECL_ARGS);
1.306     schwarze   71: static void      termp_eo_post(DECL_ARGS);
1.241     schwarze   72: static void      termp_fd_post(DECL_ARGS);
1.31      kristaps   73: static void      termp_fo_post(DECL_ARGS);
                     74: static void      termp_in_post(DECL_ARGS);
                     75: static void      termp_it_post(DECL_ARGS);
                     76: static void      termp_lb_post(DECL_ARGS);
1.164     schwarze   77: static void      termp_nm_post(DECL_ARGS);
1.31      kristaps   78: static void      termp_pf_post(DECL_ARGS);
1.188     kristaps   79: static void      termp_quote_post(DECL_ARGS);
1.31      kristaps   80: static void      termp_sh_post(DECL_ARGS);
                     81: static void      termp_ss_post(DECL_ARGS);
1.339     schwarze   82: static void      termp_xx_post(DECL_ARGS);
1.31      kristaps   83:
1.184     kristaps   84: static int       termp__a_pre(DECL_ARGS);
1.203     kristaps   85: static int       termp__t_pre(DECL_ARGS);
1.369     schwarze   86: static int       termp_abort_pre(DECL_ARGS);
1.61      kristaps   87: static int       termp_an_pre(DECL_ARGS);
1.31      kristaps   88: static int       termp_ap_pre(DECL_ARGS);
                     89: static int       termp_bd_pre(DECL_ARGS);
                     90: static int       termp_bf_pre(DECL_ARGS);
1.159     schwarze   91: static int       termp_bk_pre(DECL_ARGS);
1.115     kristaps   92: static int       termp_bl_pre(DECL_ARGS);
1.69      kristaps   93: static int       termp_bold_pre(DECL_ARGS);
1.31      kristaps   94: static int       termp_cd_pre(DECL_ARGS);
                     95: static int       termp_d1_pre(DECL_ARGS);
1.306     schwarze   96: static int       termp_eo_pre(DECL_ARGS);
1.334     schwarze   97: static int       termp_em_pre(DECL_ARGS);
1.323     schwarze   98: static int       termp_er_pre(DECL_ARGS);
1.31      kristaps   99: static int       termp_ex_pre(DECL_ARGS);
                    100: static int       termp_fa_pre(DECL_ARGS);
1.188     kristaps  101: static int       termp_fd_pre(DECL_ARGS);
1.31      kristaps  102: static int       termp_fl_pre(DECL_ARGS);
                    103: static int       termp_fn_pre(DECL_ARGS);
                    104: static int       termp_fo_pre(DECL_ARGS);
                    105: static int       termp_ft_pre(DECL_ARGS);
                    106: static int       termp_in_pre(DECL_ARGS);
                    107: static int       termp_it_pre(DECL_ARGS);
1.102     kristaps  108: static int       termp_li_pre(DECL_ARGS);
1.31      kristaps  109: static int       termp_lk_pre(DECL_ARGS);
                    110: static int       termp_nd_pre(DECL_ARGS);
                    111: static int       termp_nm_pre(DECL_ARGS);
                    112: static int       termp_ns_pre(DECL_ARGS);
1.188     kristaps  113: static int       termp_quote_pre(DECL_ARGS);
1.31      kristaps  114: static int       termp_rs_pre(DECL_ARGS);
                    115: static int       termp_sh_pre(DECL_ARGS);
1.294     schwarze  116: static int       termp_skip_pre(DECL_ARGS);
1.31      kristaps  117: static int       termp_sm_pre(DECL_ARGS);
1.355     schwarze  118: static int       termp_pp_pre(DECL_ARGS);
1.31      kristaps  119: static int       termp_ss_pre(DECL_ARGS);
1.334     schwarze  120: static int       termp_sy_pre(DECL_ARGS);
1.320     schwarze  121: static int       termp_tag_pre(DECL_ARGS);
1.375     schwarze  122: static int       termp_tg_pre(DECL_ARGS);
1.69      kristaps  123: static int       termp_under_pre(DECL_ARGS);
1.110     kristaps  124: static int       termp_vt_pre(DECL_ARGS);
1.31      kristaps  125: static int       termp_xr_pre(DECL_ARGS);
                    126: static int       termp_xx_pre(DECL_ARGS);
                    127:
1.368     schwarze  128: static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
1.1       kristaps  129:        { NULL, NULL }, /* Dd */
                    130:        { NULL, NULL }, /* Dt */
                    131:        { NULL, NULL }, /* Os */
                    132:        { termp_sh_pre, termp_sh_post }, /* Sh */
1.264     schwarze  133:        { termp_ss_pre, termp_ss_post }, /* Ss */
1.355     schwarze  134:        { termp_pp_pre, NULL }, /* Pp */
1.241     schwarze  135:        { termp_d1_pre, termp_bl_post }, /* D1 */
                    136:        { termp_d1_pre, termp_bl_post }, /* Dl */
1.1       kristaps  137:        { termp_bd_pre, termp_bd_post }, /* Bd */
                    138:        { NULL, NULL }, /* Ed */
1.115     kristaps  139:        { termp_bl_pre, termp_bl_post }, /* Bl */
1.1       kristaps  140:        { NULL, NULL }, /* El */
                    141:        { termp_it_pre, termp_it_post }, /* It */
1.264     schwarze  142:        { termp_under_pre, NULL }, /* Ad */
1.282     schwarze  143:        { termp_an_pre, NULL }, /* An */
1.350     schwarze  144:        { termp_ap_pre, NULL }, /* Ap */
1.69      kristaps  145:        { termp_under_pre, NULL }, /* Ar */
1.1       kristaps  146:        { termp_cd_pre, NULL }, /* Cd */
1.69      kristaps  147:        { termp_bold_pre, NULL }, /* Cm */
1.325     schwarze  148:        { termp_li_pre, NULL }, /* Dv */
1.323     schwarze  149:        { termp_er_pre, NULL }, /* Er */
1.320     schwarze  150:        { termp_tag_pre, NULL }, /* Ev */
1.1       kristaps  151:        { termp_ex_pre, NULL }, /* Ex */
1.264     schwarze  152:        { termp_fa_pre, NULL }, /* Fa */
                    153:        { termp_fd_pre, termp_fd_post }, /* Fd */
1.1       kristaps  154:        { termp_fl_pre, NULL }, /* Fl */
1.264     schwarze  155:        { termp_fn_pre, NULL }, /* Fn */
                    156:        { termp_ft_pre, NULL }, /* Ft */
                    157:        { termp_bold_pre, NULL }, /* Ic */
                    158:        { termp_in_pre, termp_in_post }, /* In */
1.102     kristaps  159:        { termp_li_pre, NULL }, /* Li */
1.264     schwarze  160:        { termp_nd_pre, NULL }, /* Nd */
                    161:        { termp_nm_pre, termp_nm_post }, /* Nm */
1.188     kristaps  162:        { termp_quote_pre, termp_quote_post }, /* Op */
1.369     schwarze  163:        { termp_abort_pre, NULL }, /* Ot */
1.69      kristaps  164:        { termp_under_pre, NULL }, /* Pa */
1.341     schwarze  165:        { termp_ex_pre, NULL }, /* Rv */
1.264     schwarze  166:        { NULL, NULL }, /* St */
1.69      kristaps  167:        { termp_under_pre, NULL }, /* Va */
1.143     kristaps  168:        { termp_vt_pre, NULL }, /* Vt */
1.1       kristaps  169:        { termp_xr_pre, NULL }, /* Xr */
1.184     kristaps  170:        { termp__a_pre, termp____post }, /* %A */
1.84      kristaps  171:        { termp_under_pre, termp____post }, /* %B */
1.1       kristaps  172:        { NULL, termp____post }, /* %D */
1.84      kristaps  173:        { termp_under_pre, termp____post }, /* %I */
1.69      kristaps  174:        { termp_under_pre, termp____post }, /* %J */
1.1       kristaps  175:        { NULL, termp____post }, /* %N */
                    176:        { NULL, termp____post }, /* %O */
                    177:        { NULL, termp____post }, /* %P */
                    178:        { NULL, termp____post }, /* %R */
1.203     kristaps  179:        { termp__t_pre, termp__t_post }, /* %T */
1.1       kristaps  180:        { NULL, termp____post }, /* %V */
                    181:        { NULL, NULL }, /* Ac */
1.188     kristaps  182:        { termp_quote_pre, termp_quote_post }, /* Ao */
                    183:        { termp_quote_pre, termp_quote_post }, /* Aq */
1.35      kristaps  184:        { NULL, NULL }, /* At */
1.1       kristaps  185:        { NULL, NULL }, /* Bc */
1.264     schwarze  186:        { termp_bf_pre, NULL }, /* Bf */
1.188     kristaps  187:        { termp_quote_pre, termp_quote_post }, /* Bo */
                    188:        { termp_quote_pre, termp_quote_post }, /* Bq */
1.339     schwarze  189:        { termp_xx_pre, termp_xx_post }, /* Bsx */
1.340     schwarze  190:        { NULL, NULL }, /* Bx */
1.294     schwarze  191:        { termp_skip_pre, NULL }, /* Db */
1.1       kristaps  192:        { NULL, NULL }, /* Dc */
1.188     kristaps  193:        { termp_quote_pre, termp_quote_post }, /* Do */
                    194:        { termp_quote_pre, termp_quote_post }, /* Dq */
1.112     kristaps  195:        { NULL, NULL }, /* Ec */ /* FIXME: no space */
1.1       kristaps  196:        { NULL, NULL }, /* Ef */
1.334     schwarze  197:        { termp_em_pre, NULL }, /* Em */
1.306     schwarze  198:        { termp_eo_pre, termp_eo_post }, /* Eo */
1.339     schwarze  199:        { termp_xx_pre, termp_xx_post }, /* Fx */
1.176     kristaps  200:        { termp_bold_pre, NULL }, /* Ms */
1.290     schwarze  201:        { termp_li_pre, NULL }, /* No */
1.1       kristaps  202:        { termp_ns_pre, NULL }, /* Ns */
1.339     schwarze  203:        { termp_xx_pre, termp_xx_post }, /* Nx */
                    204:        { termp_xx_pre, termp_xx_post }, /* Ox */
1.1       kristaps  205:        { NULL, NULL }, /* Pc */
1.254     schwarze  206:        { NULL, termp_pf_post }, /* Pf */
1.188     kristaps  207:        { termp_quote_pre, termp_quote_post }, /* Po */
                    208:        { termp_quote_pre, termp_quote_post }, /* Pq */
1.1       kristaps  209:        { NULL, NULL }, /* Qc */
1.188     kristaps  210:        { termp_quote_pre, termp_quote_post }, /* Ql */
                    211:        { termp_quote_pre, termp_quote_post }, /* Qo */
                    212:        { termp_quote_pre, termp_quote_post }, /* Qq */
1.1       kristaps  213:        { NULL, NULL }, /* Re */
                    214:        { termp_rs_pre, NULL }, /* Rs */
                    215:        { NULL, NULL }, /* Sc */
1.188     kristaps  216:        { termp_quote_pre, termp_quote_post }, /* So */
                    217:        { termp_quote_pre, termp_quote_post }, /* Sq */
1.1       kristaps  218:        { termp_sm_pre, NULL }, /* Sm */
1.69      kristaps  219:        { termp_under_pre, NULL }, /* Sx */
1.334     schwarze  220:        { termp_sy_pre, NULL }, /* Sy */
1.1       kristaps  221:        { NULL, NULL }, /* Tn */
1.339     schwarze  222:        { termp_xx_pre, termp_xx_post }, /* Ux */
1.1       kristaps  223:        { NULL, NULL }, /* Xc */
                    224:        { NULL, NULL }, /* Xo */
1.264     schwarze  225:        { termp_fo_pre, termp_fo_post }, /* Fo */
                    226:        { NULL, NULL }, /* Fc */
1.188     kristaps  227:        { termp_quote_pre, termp_quote_post }, /* Oo */
1.1       kristaps  228:        { NULL, NULL }, /* Oc */
1.159     schwarze  229:        { termp_bk_pre, termp_bk_post }, /* Bk */
1.1       kristaps  230:        { NULL, NULL }, /* Ek */
1.341     schwarze  231:        { NULL, NULL }, /* Bt */
1.1       kristaps  232:        { NULL, NULL }, /* Hf */
1.268     schwarze  233:        { termp_under_pre, NULL }, /* Fr */
1.341     schwarze  234:        { NULL, NULL }, /* Ud */
1.37      kristaps  235:        { NULL, termp_lb_post }, /* Lb */
1.369     schwarze  236:        { termp_abort_pre, NULL }, /* Lp */
1.264     schwarze  237:        { termp_lk_pre, NULL }, /* Lk */
                    238:        { termp_under_pre, NULL }, /* Mt */
                    239:        { termp_quote_pre, termp_quote_post }, /* Brq */
                    240:        { termp_quote_pre, termp_quote_post }, /* Bro */
                    241:        { NULL, NULL }, /* Brc */
                    242:        { NULL, termp____post }, /* %C */
1.294     schwarze  243:        { termp_skip_pre, NULL }, /* Es */
1.268     schwarze  244:        { termp_quote_pre, termp_quote_post }, /* En */
1.339     schwarze  245:        { termp_xx_pre, termp_xx_post }, /* Dx */
1.264     schwarze  246:        { NULL, termp____post }, /* %Q */
                    247:        { NULL, termp____post }, /* %U */
                    248:        { NULL, NULL }, /* Ta */
1.375     schwarze  249:        { termp_tg_pre, NULL }, /* Tg */
1.1       kristaps  250: };
                    251:
1.376   ! schwarze  252: static int      fn_prio = TAG_STRONG;
1.1       kristaps  253:
1.350     schwarze  254:
1.70      kristaps  255: void
1.371     schwarze  256: terminal_mdoc(void *arg, const struct roff_meta *mdoc)
1.1       kristaps  257: {
1.374     schwarze  258:        struct roff_node        *n, *nn;
1.89      kristaps  259:        struct termp            *p;
1.342     schwarze  260:        size_t                   save_defindent;
1.89      kristaps  261:
                    262:        p = (struct termp *)arg;
1.362     schwarze  263:        p->tcol->rmargin = p->maxrmargin = p->defrmargin;
1.356     schwarze  264:        term_tab_set(p, NULL);
                    265:        term_tab_set(p, "T");
                    266:        term_tab_set(p, ".5i");
1.70      kristaps  267:
1.319     schwarze  268:        n = mdoc->first->child;
1.281     schwarze  269:        if (p->synopsisonly) {
1.374     schwarze  270:                for (nn = NULL; n != NULL; n = n->next) {
                    271:                        if (n->tok != MDOC_Sh)
                    272:                                continue;
                    273:                        if (n->sec == SEC_SYNOPSIS)
1.281     schwarze  274:                                break;
1.374     schwarze  275:                        if (nn == NULL && n->sec == SEC_NAME)
                    276:                                nn = n;
1.281     schwarze  277:                }
1.374     schwarze  278:                if (n == NULL)
                    279:                        n = nn;
                    280:                p->flags |= TERMP_NOSPACE;
                    281:                if (n != NULL && (n = n->child->next->child) != NULL)
                    282:                        print_mdoc_nodelist(p, NULL, mdoc, n);
                    283:                term_newln(p);
1.281     schwarze  284:        } else {
1.342     schwarze  285:                save_defindent = p->defindent;
1.281     schwarze  286:                if (p->defindent == 0)
                    287:                        p->defindent = 5;
1.371     schwarze  288:                term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
1.367     schwarze  289:                while (n != NULL &&
                    290:                    (n->type == ROFFT_COMMENT ||
                    291:                     n->flags & NODE_NOPRT))
1.337     schwarze  292:                        n = n->next;
1.281     schwarze  293:                if (n != NULL) {
                    294:                        if (n->tok != MDOC_Sh)
                    295:                                term_vspace(p);
1.371     schwarze  296:                        print_mdoc_nodelist(p, NULL, mdoc, n);
1.281     schwarze  297:                }
                    298:                term_end(p);
1.342     schwarze  299:                p->defindent = save_defindent;
1.271     schwarze  300:        }
1.1       kristaps  301: }
                    302:
1.3       kristaps  303: static void
1.102     kristaps  304: print_mdoc_nodelist(DECL_ARGS)
1.1       kristaps  305: {
                    306:
1.304     schwarze  307:        while (n != NULL) {
                    308:                print_mdoc_node(p, pair, meta, n);
                    309:                n = n->next;
                    310:        }
1.1       kristaps  311: }
                    312:
1.3       kristaps  313: static void
1.102     kristaps  314: print_mdoc_node(DECL_ARGS)
1.1       kristaps  315: {
1.368     schwarze  316:        const struct mdoc_term_act *act;
1.1       kristaps  317:        struct termpair  npair;
1.30      kristaps  318:        size_t           offset, rmargin;
1.368     schwarze  319:        int              chld;
1.1       kristaps  320:
1.372     schwarze  321:        /*
                    322:         * In no-fill mode, break the output line at the beginning
                    323:         * of new input lines except after \c, and nowhere else.
                    324:         */
                    325:
                    326:        if (n->flags & NODE_NOFILL) {
                    327:                if (n->flags & NODE_LINE &&
                    328:                    (p->flags & TERMP_NONEWLINE) == 0)
                    329:                        term_newln(p);
                    330:                p->flags |= TERMP_BRNEVER;
                    331:        } else
                    332:                p->flags &= ~TERMP_BRNEVER;
                    333:
1.367     schwarze  334:        if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1.337     schwarze  335:                return;
                    336:
1.70      kristaps  337:        chld = 1;
1.362     schwarze  338:        offset = p->tcol->offset;
                    339:        rmargin = p->tcol->rmargin;
1.338     schwarze  340:        n->flags &= ~NODE_ENDED;
1.305     schwarze  341:        n->prev_font = p->fonti;
1.29      kristaps  342:
1.97      kristaps  343:        memset(&npair, 0, sizeof(struct termpair));
1.1       kristaps  344:        npair.ppair = pair;
1.172     kristaps  345:
                    346:        /*
                    347:         * Keeps only work until the end of a line.  If a keep was
                    348:         * invoked in a prior line, revert it to PREKEEP.
                    349:         */
                    350:
1.338     schwarze  351:        if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
1.307     schwarze  352:                p->flags &= ~TERMP_KEEP;
                    353:                p->flags |= TERMP_PREKEEP;
1.172     kristaps  354:        }
1.197     schwarze  355:
                    356:        /*
1.217     schwarze  357:         * After the keep flags have been set up, we may now
                    358:         * produce output.  Note that some pre-handlers do so.
                    359:         */
                    360:
1.373     schwarze  361:        act = NULL;
1.217     schwarze  362:        switch (n->type) {
1.314     schwarze  363:        case ROFFT_TEXT:
1.372     schwarze  364:                if (n->flags & NODE_LINE) {
                    365:                        switch (*n->string) {
                    366:                        case '\0':
                    367:                                if (p->flags & TERMP_NONEWLINE)
                    368:                                        term_newln(p);
                    369:                                else
                    370:                                        term_vspace(p);
                    371:                                return;
                    372:                        case ' ':
                    373:                                if ((p->flags & TERMP_NONEWLINE) == 0)
                    374:                                        term_newln(p);
                    375:                                break;
                    376:                        default:
                    377:                                break;
                    378:                        }
                    379:                }
1.338     schwarze  380:                if (NODE_DELIMC & n->flags)
1.222     kristaps  381:                        p->flags |= TERMP_NOSPACE;
1.217     schwarze  382:                term_word(p, n->string);
1.338     schwarze  383:                if (NODE_DELIMO & n->flags)
1.222     kristaps  384:                        p->flags |= TERMP_NOSPACE;
1.217     schwarze  385:                break;
1.314     schwarze  386:        case ROFFT_EQN:
1.338     schwarze  387:                if ( ! (n->flags & NODE_LINE))
1.284     schwarze  388:                        p->flags |= TERMP_NOSPACE;
1.233     kristaps  389:                term_eqn(p, n->eqn);
1.338     schwarze  390:                if (n->next != NULL && ! (n->next->flags & NODE_LINE))
1.285     schwarze  391:                        p->flags |= TERMP_NOSPACE;
1.217     schwarze  392:                break;
1.314     schwarze  393:        case ROFFT_TBL:
1.313     schwarze  394:                if (p->tbl.cols == NULL)
                    395:                        term_newln(p);
1.217     schwarze  396:                term_tbl(p, n->span);
                    397:                break;
                    398:        default:
1.351     schwarze  399:                if (n->tok < ROFF_MAX) {
1.352     schwarze  400:                        roff_term_pre(p, n);
1.354     schwarze  401:                        return;
1.351     schwarze  402:                }
                    403:                assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
1.368     schwarze  404:                act = mdoc_term_acts + (n->tok - MDOC_Dd);
                    405:                if (act->pre != NULL &&
1.331     schwarze  406:                    (n->end == ENDBODY_NOT || n->child != NULL))
1.368     schwarze  407:                        chld = (*act->pre)(p, &npair, meta, n);
1.217     schwarze  408:                break;
                    409:        }
                    410:
1.86      kristaps  411:        if (chld && n->child)
1.245     schwarze  412:                print_mdoc_nodelist(p, &npair, meta, n->child);
1.1       kristaps  413:
1.244     schwarze  414:        term_fontpopq(p,
1.310     schwarze  415:            (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
1.46      kristaps  416:
1.206     kristaps  417:        switch (n->type) {
1.314     schwarze  418:        case ROFFT_TEXT:
1.206     kristaps  419:                break;
1.314     schwarze  420:        case ROFFT_TBL:
1.216     kristaps  421:                break;
1.314     schwarze  422:        case ROFFT_EQN:
1.206     kristaps  423:                break;
                    424:        default:
1.368     schwarze  425:                if (act->post == NULL || n->flags & NODE_ENDED)
1.206     kristaps  426:                        break;
1.368     schwarze  427:                (void)(*act->post)(p, &npair, meta, n);
1.162     schwarze  428:
                    429:                /*
                    430:                 * Explicit end tokens not only call the post
                    431:                 * handler, but also tell the respective block
                    432:                 * that it must not call the post handler again.
                    433:                 */
1.165     kristaps  434:                if (ENDBODY_NOT != n->end)
1.338     schwarze  435:                        n->body->flags |= NODE_ENDED;
1.206     kristaps  436:                break;
1.162     schwarze  437:        }
1.121     kristaps  438:
1.338     schwarze  439:        if (NODE_EOS & n->flags)
1.121     kristaps  440:                p->flags |= TERMP_SENTENCE;
1.29      kristaps  441:
1.359     schwarze  442:        if (n->type != ROFFT_TEXT)
1.362     schwarze  443:                p->tcol->offset = offset;
                    444:        p->tcol->rmargin = rmargin;
1.1       kristaps  445: }
                    446:
1.3       kristaps  447: static void
1.316     schwarze  448: print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
1.1       kristaps  449: {
1.292     schwarze  450:        size_t sz;
1.144     kristaps  451:
1.102     kristaps  452:        term_fontrepl(p, TERMFONT_NONE);
1.101     kristaps  453:
1.264     schwarze  454:        /*
1.9       kristaps  455:         * Output the footer in new-groff style, that is, three columns
                    456:         * with the middle being the manual date and flanking columns
                    457:         * being the operating system:
                    458:         *
                    459:         * SYSTEM                  DATE                    SYSTEM
                    460:         */
                    461:
1.1       kristaps  462:        term_vspace(p);
                    463:
1.362     schwarze  464:        p->tcol->offset = 0;
1.292     schwarze  465:        sz = term_strlen(p, meta->date);
1.362     schwarze  466:        p->tcol->rmargin = p->maxrmargin > sz ?
1.292     schwarze  467:            (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
1.250     schwarze  468:        p->trailspace = 1;
1.1       kristaps  469:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    470:
1.245     schwarze  471:        term_word(p, meta->os);
1.1       kristaps  472:        term_flushln(p);
                    473:
1.362     schwarze  474:        p->tcol->offset = p->tcol->rmargin;
1.292     schwarze  475:        sz = term_strlen(p, meta->os);
1.362     schwarze  476:        p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
1.234     schwarze  477:        p->flags |= TERMP_NOSPACE;
1.9       kristaps  478:
1.245     schwarze  479:        term_word(p, meta->date);
1.9       kristaps  480:        term_flushln(p);
                    481:
1.362     schwarze  482:        p->tcol->offset = p->tcol->rmargin;
                    483:        p->tcol->rmargin = p->maxrmargin;
1.250     schwarze  484:        p->trailspace = 0;
1.1       kristaps  485:        p->flags &= ~TERMP_NOBREAK;
1.234     schwarze  486:        p->flags |= TERMP_NOSPACE;
1.1       kristaps  487:
1.245     schwarze  488:        term_word(p, meta->os);
1.1       kristaps  489:        term_flushln(p);
                    490:
1.362     schwarze  491:        p->tcol->offset = 0;
                    492:        p->tcol->rmargin = p->maxrmargin;
1.9       kristaps  493:        p->flags = 0;
1.1       kristaps  494: }
                    495:
1.3       kristaps  496: static void
1.316     schwarze  497: print_mdoc_head(struct termp *p, const struct roff_meta *meta)
1.1       kristaps  498: {
1.267     schwarze  499:        char                    *volume, *title;
                    500:        size_t                   vollen, titlen;
1.1       kristaps  501:
                    502:        /*
                    503:         * The header is strange.  It has three components, which are
                    504:         * really two with the first duplicated.  It goes like this:
                    505:         *
                    506:         * IDENTIFIER              TITLE                   IDENTIFIER
                    507:         *
                    508:         * The IDENTIFIER is NAME(SECTION), which is the command-name
                    509:         * (if given, or "unknown" if not) followed by the manual page
                    510:         * section.  These are given in `Dt'.  The TITLE is a free-form
                    511:         * string depending on the manual volume.  If not specified, it
                    512:         * switches on the manual section.
                    513:         */
1.235     schwarze  514:
1.245     schwarze  515:        assert(meta->vol);
1.267     schwarze  516:        if (NULL == meta->arch)
                    517:                volume = mandoc_strdup(meta->vol);
                    518:        else
                    519:                mandoc_asprintf(&volume, "%s (%s)",
                    520:                    meta->vol, meta->arch);
                    521:        vollen = term_strlen(p, volume);
1.1       kristaps  522:
1.275     schwarze  523:        if (NULL == meta->msec)
                    524:                title = mandoc_strdup(meta->title);
                    525:        else
                    526:                mandoc_asprintf(&title, "%s(%s)",
                    527:                    meta->title, meta->msec);
1.235     schwarze  528:        titlen = term_strlen(p, title);
1.1       kristaps  529:
1.235     schwarze  530:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.250     schwarze  531:        p->trailspace = 1;
1.362     schwarze  532:        p->tcol->offset = 0;
                    533:        p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1.267     schwarze  534:            (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1.292     schwarze  535:            vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
1.1       kristaps  536:
                    537:        term_word(p, title);
                    538:        term_flushln(p);
                    539:
1.235     schwarze  540:        p->flags |= TERMP_NOSPACE;
1.362     schwarze  541:        p->tcol->offset = p->tcol->rmargin;
                    542:        p->tcol->rmargin = p->tcol->offset + vollen + titlen <
                    543:            p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1.1       kristaps  544:
1.267     schwarze  545:        term_word(p, volume);
1.1       kristaps  546:        term_flushln(p);
                    547:
                    548:        p->flags &= ~TERMP_NOBREAK;
1.250     schwarze  549:        p->trailspace = 0;
1.362     schwarze  550:        if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1.235     schwarze  551:                p->flags |= TERMP_NOSPACE;
1.362     schwarze  552:                p->tcol->offset = p->tcol->rmargin;
                    553:                p->tcol->rmargin = p->maxrmargin;
1.235     schwarze  554:                term_word(p, title);
                    555:                term_flushln(p);
                    556:        }
1.1       kristaps  557:
1.235     schwarze  558:        p->flags &= ~TERMP_NOSPACE;
1.362     schwarze  559:        p->tcol->offset = 0;
                    560:        p->tcol->rmargin = p->maxrmargin;
1.266     schwarze  561:        free(title);
1.267     schwarze  562:        free(volume);
1.1       kristaps  563: }
                    564:
1.302     schwarze  565: static int
1.157     kristaps  566: a2width(const struct termp *p, const char *v)
1.91      kristaps  567: {
1.92      kristaps  568:        struct roffsu    su;
1.363     schwarze  569:        const char      *end;
1.1       kristaps  570:
1.363     schwarze  571:        end = a2roffsu(v, &su, SCALE_MAX);
                    572:        if (end == NULL || *end != '\0') {
1.157     kristaps  573:                SCALE_HS_INIT(&su, term_strlen(p, v));
1.283     schwarze  574:                su.scale /= term_strlen(p, "0");
1.302     schwarze  575:        }
1.364     schwarze  576:        return term_hen(p, &su);
1.1       kristaps  577: }
                    578:
1.108     kristaps  579: /*
                    580:  * Determine how much space to print out before block elements of `It'
                    581:  * (and thus `Bl') and `Bd'.  And then go ahead and print that space,
                    582:  * too.
                    583:  */
1.54      kristaps  584: static void
1.264     schwarze  585: print_bvspace(struct termp *p,
1.315     schwarze  586:        const struct roff_node *bl,
                    587:        const struct roff_node *n)
1.1       kristaps  588: {
1.315     schwarze  589:        const struct roff_node  *nn;
1.236     schwarze  590:
                    591:        assert(n);
1.1       kristaps  592:
                    593:        term_newln(p);
1.150     kristaps  594:
1.202     kristaps  595:        if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
1.150     kristaps  596:                return;
1.202     kristaps  597:        if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
1.54      kristaps  598:                return;
                    599:
1.85      kristaps  600:        /* Do not vspace directly after Ss/Sh. */
1.1       kristaps  601:
1.289     schwarze  602:        nn = n;
1.367     schwarze  603:        while (nn->prev != NULL &&
                    604:            (nn->prev->type == ROFFT_COMMENT ||
                    605:             nn->prev->flags & NODE_NOPRT))
1.337     schwarze  606:                nn = nn->prev;
1.289     schwarze  607:        while (nn->prev == NULL) {
                    608:                do {
                    609:                        nn = nn->parent;
1.314     schwarze  610:                        if (nn->type == ROFFT_ROOT)
1.289     schwarze  611:                                return;
1.314     schwarze  612:                } while (nn->type != ROFFT_BLOCK);
1.289     schwarze  613:                if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
1.54      kristaps  614:                        return;
1.289     schwarze  615:                if (nn->tok == MDOC_It &&
                    616:                    nn->parent->parent->norm->Bl.type != LIST_item)
                    617:                        break;
1.1       kristaps  618:        }
                    619:
1.85      kristaps  620:        /* A `-column' does not assert vspace within the list. */
1.54      kristaps  621:
1.202     kristaps  622:        if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
1.86      kristaps  623:                if (n->prev && MDOC_It == n->prev->tok)
1.54      kristaps  624:                        return;
                    625:
1.85      kristaps  626:        /* A `-diag' without body does not vspace. */
                    627:
1.202     kristaps  628:        if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
1.86      kristaps  629:                if (n->prev && MDOC_It == n->prev->tok) {
                    630:                        assert(n->prev->body);
                    631:                        if (NULL == n->prev->body->child)
1.55      kristaps  632:                                return;
                    633:                }
                    634:
1.54      kristaps  635:        term_vspace(p);
1.260     schwarze  636: }
                    637:
1.1       kristaps  638:
                    639: static int
                    640: termp_it_pre(DECL_ARGS)
                    641: {
1.345     schwarze  642:        struct roffsu           su;
1.302     schwarze  643:        char                    buf[24];
1.315     schwarze  644:        const struct roff_node *bl, *nn;
1.302     schwarze  645:        size_t                  ncols, dcol;
                    646:        int                     i, offset, width;
1.126     kristaps  647:        enum mdoc_list          type;
1.1       kristaps  648:
1.314     schwarze  649:        if (n->type == ROFFT_BLOCK) {
1.151     kristaps  650:                print_bvspace(p, n->parent->parent, n);
1.328     schwarze  651:                return 1;
1.54      kristaps  652:        }
1.1       kristaps  653:
1.86      kristaps  654:        bl = n->parent->parent->parent;
1.202     kristaps  655:        type = bl->norm->Bl.type;
1.1       kristaps  656:
1.264     schwarze  657:        /*
1.302     schwarze  658:         * Defaults for specific list types.
                    659:         */
                    660:
                    661:        switch (type) {
                    662:        case LIST_bullet:
                    663:        case LIST_dash:
                    664:        case LIST_hyphen:
                    665:        case LIST_enum:
                    666:                width = term_len(p, 2);
                    667:                break;
                    668:        case LIST_hang:
1.332     schwarze  669:        case LIST_tag:
1.302     schwarze  670:                width = term_len(p, 8);
                    671:                break;
                    672:        case LIST_column:
                    673:                width = term_len(p, 10);
                    674:                break;
                    675:        default:
                    676:                width = 0;
                    677:                break;
                    678:        }
                    679:        offset = 0;
                    680:
                    681:        /*
1.108     kristaps  682:         * First calculate width and offset.  This is pretty easy unless
                    683:         * we're a -column list, in which case all prior columns must
                    684:         * be accounted for.
                    685:         */
                    686:
1.302     schwarze  687:        if (bl->norm->Bl.offs != NULL) {
1.288     schwarze  688:                offset = a2width(p, bl->norm->Bl.offs);
1.362     schwarze  689:                if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
                    690:                        offset = -p->tcol->offset;
1.302     schwarze  691:                else if (offset > SHRT_MAX)
                    692:                        offset = 0;
                    693:        }
1.152     kristaps  694:
1.1       kristaps  695:        switch (type) {
1.264     schwarze  696:        case LIST_column:
1.314     schwarze  697:                if (n->type == ROFFT_HEAD)
1.1       kristaps  698:                        break;
1.155     kristaps  699:
1.105     kristaps  700:                /*
1.108     kristaps  701:                 * Imitate groff's column handling:
                    702:                 * - For each earlier column, add its width.
                    703:                 * - For less than 5 columns, add four more blanks per
                    704:                 *   column.
                    705:                 * - For exactly 5 columns, add three more blank per
                    706:                 *   column.
                    707:                 * - For more than 5 columns, add only one column.
1.105     kristaps  708:                 */
1.202     kristaps  709:                ncols = bl->norm->Bl.ncols;
1.264     schwarze  710:                dcol = ncols < 5 ? term_len(p, 4) :
                    711:                    ncols == 5 ? term_len(p, 3) : term_len(p, 1);
1.108     kristaps  712:
1.134     kristaps  713:                /*
1.314     schwarze  714:                 * Calculate the offset by applying all prior ROFFT_BODY,
                    715:                 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
1.134     kristaps  716:                 */
                    717:
1.264     schwarze  718:                for (i = 0, nn = n->prev;
                    719:                    nn->prev && i < (int)ncols;
1.345     schwarze  720:                    nn = nn->prev, i++) {
                    721:                        SCALE_HS_INIT(&su,
                    722:                            term_strlen(p, bl->norm->Bl.cols[i]));
                    723:                        su.scale /= term_strlen(p, "0");
1.364     schwarze  724:                        offset += term_hen(p, &su) + dcol;
1.345     schwarze  725:                }
1.105     kristaps  726:
                    727:                /*
1.108     kristaps  728:                 * When exceeding the declared number of columns, leave
                    729:                 * the remaining widths at 0.  This will later be
                    730:                 * adjusted to the default width of 10, or, for the last
                    731:                 * column, stretched to the right margin.
1.58      kristaps  732:                 */
1.108     kristaps  733:                if (i >= (int)ncols)
                    734:                        break;
1.58      kristaps  735:
1.105     kristaps  736:                /*
1.108     kristaps  737:                 * Use the declared column widths, extended as explained
                    738:                 * in the preceding paragraph.
1.105     kristaps  739:                 */
1.345     schwarze  740:                SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
                    741:                su.scale /= term_strlen(p, "0");
1.364     schwarze  742:                width = term_hen(p, &su) + dcol;
1.1       kristaps  743:                break;
                    744:        default:
1.202     kristaps  745:                if (NULL == bl->norm->Bl.width)
1.108     kristaps  746:                        break;
                    747:
1.264     schwarze  748:                /*
1.108     kristaps  749:                 * Note: buffer the width by 2, which is groff's magic
                    750:                 * number for buffering single arguments.  See the above
                    751:                 * handling for column for how this changes.
                    752:                 */
1.202     kristaps  753:                width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
1.362     schwarze  754:                if (width < 0 && (size_t)(-width) > p->tcol->offset)
                    755:                        width = -p->tcol->offset;
1.302     schwarze  756:                else if (width > SHRT_MAX)
                    757:                        width = 0;
1.1       kristaps  758:                break;
                    759:        }
                    760:
1.264     schwarze  761:        /*
1.17      kristaps  762:         * Whitespace control.  Inset bodies need an initial space,
                    763:         * while diagonal bodies need two.
1.1       kristaps  764:         */
                    765:
1.51      kristaps  766:        p->flags |= TERMP_NOSPACE;
                    767:
1.1       kristaps  768:        switch (type) {
1.264     schwarze  769:        case LIST_diag:
1.314     schwarze  770:                if (n->type == ROFFT_BODY)
1.62      kristaps  771:                        term_word(p, "\\ \\ ");
1.51      kristaps  772:                break;
1.264     schwarze  773:        case LIST_inset:
1.331     schwarze  774:                if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
1.51      kristaps  775:                        term_word(p, "\\ ");
1.1       kristaps  776:                break;
                    777:        default:
                    778:                break;
                    779:        }
                    780:
1.51      kristaps  781:        p->flags |= TERMP_NOSPACE;
                    782:
1.1       kristaps  783:        switch (type) {
1.264     schwarze  784:        case LIST_diag:
1.314     schwarze  785:                if (n->type == ROFFT_HEAD)
1.102     kristaps  786:                        term_fontpush(p, TERMFONT_BOLD);
1.1       kristaps  787:                break;
                    788:        default:
                    789:                break;
                    790:        }
                    791:
                    792:        /*
1.109     kristaps  793:         * Pad and break control.  This is the tricky part.  These flags
                    794:         * are documented in term_flushln() in term.c.  Note that we're
                    795:         * going to unset all of these flags in termp_it_post() when we
                    796:         * exit.
1.1       kristaps  797:         */
                    798:
                    799:        switch (type) {
1.264     schwarze  800:        case LIST_enum:
1.329     schwarze  801:        case LIST_bullet:
                    802:        case LIST_dash:
                    803:        case LIST_hyphen:
1.360     schwarze  804:                if (n->type == ROFFT_HEAD) {
                    805:                        p->flags |= TERMP_NOBREAK | TERMP_HANG;
                    806:                        p->trailspace = 1;
                    807:                } else if (width <= (int)term_len(p, 2))
                    808:                        p->flags |= TERMP_NOPAD;
1.39      kristaps  809:                break;
1.264     schwarze  810:        case LIST_hang:
1.314     schwarze  811:                if (n->type != ROFFT_HEAD)
1.59      kristaps  812:                        break;
1.263     schwarze  813:                p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1.250     schwarze  814:                p->trailspace = 1;
1.39      kristaps  815:                break;
1.264     schwarze  816:        case LIST_tag:
1.314     schwarze  817:                if (n->type != ROFFT_HEAD)
1.39      kristaps  818:                        break;
1.250     schwarze  819:
1.326     schwarze  820:                p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
1.250     schwarze  821:                p->trailspace = 2;
                    822:
1.86      kristaps  823:                if (NULL == n->next || NULL == n->next->child)
1.360     schwarze  824:                        p->flags |= TERMP_HANG;
1.1       kristaps  825:                break;
1.264     schwarze  826:        case LIST_column:
1.314     schwarze  827:                if (n->type == ROFFT_HEAD)
1.134     kristaps  828:                        break;
                    829:
1.250     schwarze  830:                if (NULL == n->next) {
1.134     kristaps  831:                        p->flags &= ~TERMP_NOBREAK;
1.250     schwarze  832:                        p->trailspace = 0;
                    833:                } else {
1.134     kristaps  834:                        p->flags |= TERMP_NOBREAK;
1.250     schwarze  835:                        p->trailspace = 1;
                    836:                }
1.134     kristaps  837:
1.1       kristaps  838:                break;
1.264     schwarze  839:        case LIST_diag:
1.314     schwarze  840:                if (n->type != ROFFT_HEAD)
1.250     schwarze  841:                        break;
1.263     schwarze  842:                p->flags |= TERMP_NOBREAK | TERMP_BRIND;
1.250     schwarze  843:                p->trailspace = 1;
1.1       kristaps  844:                break;
                    845:        default:
                    846:                break;
                    847:        }
                    848:
1.264     schwarze  849:        /*
1.1       kristaps  850:         * Margin control.  Set-head-width lists have their right
                    851:         * margins shortened.  The body for these lists has the offset
                    852:         * necessarily lengthened.  Everybody gets the offset.
                    853:         */
                    854:
1.362     schwarze  855:        p->tcol->offset += offset;
1.1       kristaps  856:
                    857:        switch (type) {
1.264     schwarze  858:        case LIST_bullet:
                    859:        case LIST_dash:
                    860:        case LIST_enum:
                    861:        case LIST_hyphen:
1.360     schwarze  862:        case LIST_hang:
1.264     schwarze  863:        case LIST_tag:
1.314     schwarze  864:                if (n->type == ROFFT_HEAD)
1.362     schwarze  865:                        p->tcol->rmargin = p->tcol->offset + width;
1.292     schwarze  866:                else
1.362     schwarze  867:                        p->tcol->offset += width;
1.1       kristaps  868:                break;
1.264     schwarze  869:        case LIST_column:
1.49      kristaps  870:                assert(width);
1.362     schwarze  871:                p->tcol->rmargin = p->tcol->offset + width;
1.264     schwarze  872:                /*
1.50      kristaps  873:                 * XXX - this behaviour is not documented: the
                    874:                 * right-most column is filled to the right margin.
                    875:                 */
1.314     schwarze  876:                if (n->type == ROFFT_HEAD)
1.134     kristaps  877:                        break;
1.362     schwarze  878:                if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
                    879:                        p->tcol->rmargin = p->maxrmargin;
1.1       kristaps  880:                break;
                    881:        default:
                    882:                break;
                    883:        }
                    884:
1.264     schwarze  885:        /*
1.1       kristaps  886:         * The dash, hyphen, bullet and enum lists all have a special
1.264     schwarze  887:         * HEAD character (temporarily bold, in some cases).
1.1       kristaps  888:         */
                    889:
1.314     schwarze  890:        if (n->type == ROFFT_HEAD)
1.1       kristaps  891:                switch (type) {
1.264     schwarze  892:                case LIST_bullet:
1.102     kristaps  893:                        term_fontpush(p, TERMFONT_BOLD);
1.1       kristaps  894:                        term_word(p, "\\[bu]");
1.102     kristaps  895:                        term_fontpop(p);
1.1       kristaps  896:                        break;
1.264     schwarze  897:                case LIST_dash:
                    898:                case LIST_hyphen:
1.102     kristaps  899:                        term_fontpush(p, TERMFONT_BOLD);
1.330     schwarze  900:                        term_word(p, "-");
1.102     kristaps  901:                        term_fontpop(p);
1.1       kristaps  902:                        break;
1.264     schwarze  903:                case LIST_enum:
1.1       kristaps  904:                        (pair->ppair->ppair->count)++;
1.265     schwarze  905:                        (void)snprintf(buf, sizeof(buf), "%d.",
1.264     schwarze  906:                            pair->ppair->ppair->count);
1.1       kristaps  907:                        term_word(p, buf);
                    908:                        break;
                    909:                default:
                    910:                        break;
                    911:                }
                    912:
1.264     schwarze  913:        /*
1.1       kristaps  914:         * If we're not going to process our children, indicate so here.
                    915:         */
                    916:
                    917:        switch (type) {
1.264     schwarze  918:        case LIST_bullet:
                    919:        case LIST_item:
                    920:        case LIST_dash:
                    921:        case LIST_hyphen:
                    922:        case LIST_enum:
1.314     schwarze  923:                if (n->type == ROFFT_HEAD)
1.328     schwarze  924:                        return 0;
1.1       kristaps  925:                break;
1.264     schwarze  926:        case LIST_column:
1.314     schwarze  927:                if (n->type == ROFFT_HEAD)
1.328     schwarze  928:                        return 0;
1.360     schwarze  929:                p->minbl = 0;
1.1       kristaps  930:                break;
                    931:        default:
                    932:                break;
                    933:        }
                    934:
1.328     schwarze  935:        return 1;
1.1       kristaps  936: }
                    937:
                    938: static void
                    939: termp_it_post(DECL_ARGS)
                    940: {
1.126     kristaps  941:        enum mdoc_list     type;
1.1       kristaps  942:
1.314     schwarze  943:        if (n->type == ROFFT_BLOCK)
1.1       kristaps  944:                return;
                    945:
1.202     kristaps  946:        type = n->parent->parent->parent->norm->Bl.type;
1.1       kristaps  947:
                    948:        switch (type) {
1.264     schwarze  949:        case LIST_item:
                    950:        case LIST_diag:
                    951:        case LIST_inset:
1.314     schwarze  952:                if (n->type == ROFFT_BODY)
1.130     schwarze  953:                        term_newln(p);
1.1       kristaps  954:                break;
1.264     schwarze  955:        case LIST_column:
1.314     schwarze  956:                if (n->type == ROFFT_BODY)
1.1       kristaps  957:                        term_flushln(p);
                    958:                break;
                    959:        default:
1.130     schwarze  960:                term_newln(p);
1.1       kristaps  961:                break;
                    962:        }
                    963:
1.264     schwarze  964:        /*
1.109     kristaps  965:         * Now that our output is flushed, we can reset our tags.  Since
                    966:         * only `It' sets these flags, we're free to assume that nobody
                    967:         * has munged them in the meanwhile.
                    968:         */
                    969:
1.360     schwarze  970:        p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
1.250     schwarze  971:        p->trailspace = 0;
1.1       kristaps  972: }
                    973:
                    974: static int
                    975: termp_nm_pre(DECL_ARGS)
                    976: {
1.296     schwarze  977:        const char      *cp;
1.1       kristaps  978:
1.314     schwarze  979:        if (n->type == ROFFT_BLOCK) {
1.253     schwarze  980:                p->flags |= TERMP_PREKEEP;
1.328     schwarze  981:                return 1;
1.253     schwarze  982:        }
1.164     schwarze  983:
1.314     schwarze  984:        if (n->type == ROFFT_BODY) {
1.362     schwarze  985:                if (n->child == NULL)
1.328     schwarze  986:                        return 0;
1.234     schwarze  987:                p->flags |= TERMP_NOSPACE;
1.296     schwarze  988:                cp = NULL;
                    989:                if (n->prev->child != NULL)
                    990:                    cp = n->prev->child->string;
                    991:                if (cp == NULL)
                    992:                        cp = meta->name;
                    993:                if (cp == NULL)
1.362     schwarze  994:                        p->tcol->offset += term_len(p, 6);
1.296     schwarze  995:                else
1.362     schwarze  996:                        p->tcol->offset += term_len(p, 1) +
                    997:                            term_strlen(p, cp);
1.328     schwarze  998:                return 1;
1.164     schwarze  999:        }
                   1000:
1.343     schwarze 1001:        if (n->child == NULL)
1.328     schwarze 1002:                return 0;
1.125     kristaps 1003:
1.314     schwarze 1004:        if (n->type == ROFFT_HEAD)
1.177     schwarze 1005:                synopsis_pre(p, n->parent);
1.102     kristaps 1006:
1.314     schwarze 1007:        if (n->type == ROFFT_HEAD &&
1.362     schwarze 1008:            n->next != NULL && n->next->child != NULL) {
1.263     schwarze 1009:                p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
1.250     schwarze 1010:                p->trailspace = 1;
1.362     schwarze 1011:                p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
                   1012:                if (n->child == NULL)
                   1013:                        p->tcol->rmargin += term_strlen(p, meta->name);
                   1014:                else if (n->child->type == ROFFT_TEXT) {
                   1015:                        p->tcol->rmargin += term_strlen(p, n->child->string);
                   1016:                        if (n->child->next != NULL)
1.185     schwarze 1017:                                p->flags |= TERMP_HANG;
                   1018:                } else {
1.362     schwarze 1019:                        p->tcol->rmargin += term_len(p, 5);
1.185     schwarze 1020:                        p->flags |= TERMP_HANG;
                   1021:                }
1.164     schwarze 1022:        }
                   1023:
1.102     kristaps 1024:        term_fontpush(p, TERMFONT_BOLD);
1.328     schwarze 1025:        return 1;
1.1       kristaps 1026: }
                   1027:
1.164     schwarze 1028: static void
                   1029: termp_nm_post(DECL_ARGS)
                   1030: {
                   1031:
1.314     schwarze 1032:        if (n->type == ROFFT_BLOCK) {
1.253     schwarze 1033:                p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1.314     schwarze 1034:        } else if (n->type == ROFFT_HEAD &&
1.278     schwarze 1035:            NULL != n->next && NULL != n->next->child) {
1.164     schwarze 1036:                term_flushln(p);
1.263     schwarze 1037:                p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1.250     schwarze 1038:                p->trailspace = 0;
1.314     schwarze 1039:        } else if (n->type == ROFFT_BODY && n->child != NULL)
1.164     schwarze 1040:                term_flushln(p);
                   1041: }
                   1042:
1.1       kristaps 1043: static int
                   1044: termp_fl_pre(DECL_ARGS)
                   1045: {
                   1046:
1.320     schwarze 1047:        termp_tag_pre(p, pair, meta, n);
1.102     kristaps 1048:        term_fontpush(p, TERMFONT_BOLD);
1.1       kristaps 1049:        term_word(p, "\\-");
1.103     kristaps 1050:
1.331     schwarze 1051:        if (!(n->child == NULL &&
1.280     schwarze 1052:            (n->next == NULL ||
1.314     schwarze 1053:             n->next->type == ROFFT_TEXT ||
1.338     schwarze 1054:             n->next->flags & NODE_LINE)))
1.103     kristaps 1055:                p->flags |= TERMP_NOSPACE;
                   1056:
1.328     schwarze 1057:        return 1;
1.1       kristaps 1058: }
                   1059:
                   1060: static int
1.184     kristaps 1061: termp__a_pre(DECL_ARGS)
                   1062: {
                   1063:
                   1064:        if (n->prev && MDOC__A == n->prev->tok)
                   1065:                if (NULL == n->next || MDOC__A != n->next->tok)
                   1066:                        term_word(p, "and");
                   1067:
1.328     schwarze 1068:        return 1;
1.184     kristaps 1069: }
                   1070:
                   1071: static int
1.61      kristaps 1072: termp_an_pre(DECL_ARGS)
                   1073: {
                   1074:
1.282     schwarze 1075:        if (n->norm->An.auth == AUTH_split) {
                   1076:                p->flags &= ~TERMP_NOSPLIT;
                   1077:                p->flags |= TERMP_SPLIT;
1.328     schwarze 1078:                return 0;
1.282     schwarze 1079:        }
                   1080:        if (n->norm->An.auth == AUTH_nosplit) {
                   1081:                p->flags &= ~TERMP_SPLIT;
                   1082:                p->flags |= TERMP_NOSPLIT;
1.328     schwarze 1083:                return 0;
1.282     schwarze 1084:        }
1.264     schwarze 1085:
1.282     schwarze 1086:        if (p->flags & TERMP_SPLIT)
1.61      kristaps 1087:                term_newln(p);
                   1088:
1.282     schwarze 1089:        if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
                   1090:                p->flags |= TERMP_SPLIT;
1.61      kristaps 1091:
1.328     schwarze 1092:        return 1;
1.61      kristaps 1093: }
                   1094:
                   1095: static int
1.1       kristaps 1096: termp_ns_pre(DECL_ARGS)
                   1097: {
                   1098:
1.338     schwarze 1099:        if ( ! (NODE_LINE & n->flags))
1.215     kristaps 1100:                p->flags |= TERMP_NOSPACE;
1.328     schwarze 1101:        return 1;
1.1       kristaps 1102: }
                   1103:
                   1104: static int
                   1105: termp_rs_pre(DECL_ARGS)
                   1106: {
                   1107:
1.86      kristaps 1108:        if (SEC_SEE_ALSO != n->sec)
1.328     schwarze 1109:                return 1;
1.314     schwarze 1110:        if (n->type == ROFFT_BLOCK && n->prev != NULL)
1.1       kristaps 1111:                term_vspace(p);
1.328     schwarze 1112:        return 1;
1.1       kristaps 1113: }
                   1114:
                   1115: static int
                   1116: termp_ex_pre(DECL_ARGS)
                   1117: {
1.224     kristaps 1118:        term_newln(p);
1.341     schwarze 1119:        return 1;
1.1       kristaps 1120: }
                   1121:
                   1122: static int
                   1123: termp_nd_pre(DECL_ARGS)
                   1124: {
1.25      kristaps 1125:
1.314     schwarze 1126:        if (n->type == ROFFT_BODY)
1.297     schwarze 1127:                term_word(p, "\\(en");
1.328     schwarze 1128:        return 1;
1.115     kristaps 1129: }
                   1130:
                   1131: static int
                   1132: termp_bl_pre(DECL_ARGS)
                   1133: {
                   1134:
1.328     schwarze 1135:        return n->type != ROFFT_HEAD;
1.1       kristaps 1136: }
                   1137:
                   1138: static void
                   1139: termp_bl_post(DECL_ARGS)
                   1140: {
                   1141:
1.356     schwarze 1142:        if (n->type != ROFFT_BLOCK)
                   1143:                return;
                   1144:        term_newln(p);
                   1145:        if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
                   1146:                return;
                   1147:        term_tab_set(p, NULL);
                   1148:        term_tab_set(p, "T");
                   1149:        term_tab_set(p, ".5i");
1.1       kristaps 1150: }
                   1151:
                   1152: static int
                   1153: termp_xr_pre(DECL_ARGS)
                   1154: {
                   1155:
1.225     kristaps 1156:        if (NULL == (n = n->child))
1.328     schwarze 1157:                return 0;
1.113     kristaps 1158:
1.314     schwarze 1159:        assert(n->type == ROFFT_TEXT);
1.225     kristaps 1160:        term_word(p, n->string);
1.11      kristaps 1161:
1.264     schwarze 1162:        if (NULL == (n = n->next))
1.328     schwarze 1163:                return 0;
1.225     kristaps 1164:
1.1       kristaps 1165:        p->flags |= TERMP_NOSPACE;
                   1166:        term_word(p, "(");
1.222     kristaps 1167:        p->flags |= TERMP_NOSPACE;
1.225     kristaps 1168:
1.314     schwarze 1169:        assert(n->type == ROFFT_TEXT);
1.225     kristaps 1170:        term_word(p, n->string);
                   1171:
1.222     kristaps 1172:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1173:        term_word(p, ")");
1.86      kristaps 1174:
1.328     schwarze 1175:        return 0;
1.1       kristaps 1176: }
                   1177:
1.143     kristaps 1178: /*
                   1179:  * This decides how to assert whitespace before any of the SYNOPSIS set
                   1180:  * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
                   1181:  * macro combos).
                   1182:  */
                   1183: static void
1.315     schwarze 1184: synopsis_pre(struct termp *p, const struct roff_node *n)
1.143     kristaps 1185: {
1.264     schwarze 1186:        /*
1.143     kristaps 1187:         * Obviously, if we're not in a SYNOPSIS or no prior macros
                   1188:         * exist, do nothing.
                   1189:         */
1.338     schwarze 1190:        if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
1.143     kristaps 1191:                return;
                   1192:
                   1193:        /*
                   1194:         * If we're the second in a pair of like elements, emit our
                   1195:         * newline and return.  UNLESS we're `Fo', `Fn', `Fn', in which
                   1196:         * case we soldier on.
                   1197:         */
1.264     schwarze 1198:        if (n->prev->tok == n->tok &&
                   1199:            MDOC_Ft != n->tok &&
                   1200:            MDOC_Fo != n->tok &&
                   1201:            MDOC_Fn != n->tok) {
1.143     kristaps 1202:                term_newln(p);
                   1203:                return;
                   1204:        }
                   1205:
                   1206:        /*
                   1207:         * If we're one of the SYNOPSIS set and non-like pair-wise after
                   1208:         * another (or Fn/Fo, which we've let slip through) then assert
                   1209:         * vertical space, else only newline and move on.
                   1210:         */
                   1211:        switch (n->prev->tok) {
1.264     schwarze 1212:        case MDOC_Fd:
                   1213:        case MDOC_Fn:
                   1214:        case MDOC_Fo:
                   1215:        case MDOC_In:
                   1216:        case MDOC_Vt:
1.143     kristaps 1217:                term_vspace(p);
                   1218:                break;
1.264     schwarze 1219:        case MDOC_Ft:
1.143     kristaps 1220:                if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
                   1221:                        term_vspace(p);
                   1222:                        break;
                   1223:                }
                   1224:                /* FALLTHROUGH */
                   1225:        default:
                   1226:                term_newln(p);
                   1227:                break;
                   1228:        }
                   1229: }
                   1230:
1.110     kristaps 1231: static int
                   1232: termp_vt_pre(DECL_ARGS)
                   1233: {
                   1234:
1.314     schwarze 1235:        if (n->type == ROFFT_ELEM) {
1.143     kristaps 1236:                synopsis_pre(p, n);
1.328     schwarze 1237:                return termp_under_pre(p, pair, meta, n);
1.314     schwarze 1238:        } else if (n->type == ROFFT_BLOCK) {
1.143     kristaps 1239:                synopsis_pre(p, n);
1.328     schwarze 1240:                return 1;
1.314     schwarze 1241:        } else if (n->type == ROFFT_HEAD)
1.328     schwarze 1242:                return 0;
1.110     kristaps 1243:
1.328     schwarze 1244:        return termp_under_pre(p, pair, meta, n);
1.110     kristaps 1245: }
                   1246:
1.1       kristaps 1247: static int
1.69      kristaps 1248: termp_bold_pre(DECL_ARGS)
1.1       kristaps 1249: {
                   1250:
1.320     schwarze 1251:        termp_tag_pre(p, pair, meta, n);
1.102     kristaps 1252:        term_fontpush(p, TERMFONT_BOLD);
1.328     schwarze 1253:        return 1;
1.1       kristaps 1254: }
                   1255:
1.143     kristaps 1256: static int
                   1257: termp_fd_pre(DECL_ARGS)
1.1       kristaps 1258: {
                   1259:
1.143     kristaps 1260:        synopsis_pre(p, n);
1.328     schwarze 1261:        return termp_bold_pre(p, pair, meta, n);
1.1       kristaps 1262: }
                   1263:
1.241     schwarze 1264: static void
                   1265: termp_fd_post(DECL_ARGS)
                   1266: {
                   1267:
                   1268:        term_newln(p);
                   1269: }
                   1270:
1.1       kristaps 1271: static int
                   1272: termp_sh_pre(DECL_ARGS)
                   1273: {
1.85      kristaps 1274:
1.86      kristaps 1275:        switch (n->type) {
1.314     schwarze 1276:        case ROFFT_BLOCK:
1.293     schwarze 1277:                /*
                   1278:                 * Vertical space before sections, except
                   1279:                 * when the previous section was empty.
                   1280:                 */
                   1281:                if (n->prev == NULL ||
1.322     schwarze 1282:                    n->prev->tok != MDOC_Sh ||
1.293     schwarze 1283:                    (n->prev->body != NULL &&
                   1284:                     n->prev->body->child != NULL))
                   1285:                        term_vspace(p);
1.65      kristaps 1286:                break;
1.314     schwarze 1287:        case ROFFT_HEAD:
1.102     kristaps 1288:                term_fontpush(p, TERMFONT_BOLD);
1.1       kristaps 1289:                break;
1.314     schwarze 1290:        case ROFFT_BODY:
1.362     schwarze 1291:                p->tcol->offset = term_len(p, p->defindent);
1.356     schwarze 1292:                term_tab_set(p, NULL);
                   1293:                term_tab_set(p, "T");
                   1294:                term_tab_set(p, ".5i");
1.322     schwarze 1295:                switch (n->sec) {
                   1296:                case SEC_DESCRIPTION:
1.376   ! schwarze 1297:                        fn_prio = TAG_STRONG;
1.322     schwarze 1298:                        break;
                   1299:                case SEC_AUTHORS:
1.239     schwarze 1300:                        p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1.322     schwarze 1301:                        break;
                   1302:                default:
                   1303:                        break;
                   1304:                }
1.1       kristaps 1305:                break;
                   1306:        default:
                   1307:                break;
                   1308:        }
1.328     schwarze 1309:        return 1;
1.1       kristaps 1310: }
                   1311:
                   1312: static void
                   1313: termp_sh_post(DECL_ARGS)
                   1314: {
                   1315:
1.86      kristaps 1316:        switch (n->type) {
1.314     schwarze 1317:        case ROFFT_HEAD:
1.1       kristaps 1318:                term_newln(p);
                   1319:                break;
1.314     schwarze 1320:        case ROFFT_BODY:
1.1       kristaps 1321:                term_newln(p);
1.362     schwarze 1322:                p->tcol->offset = 0;
1.1       kristaps 1323:                break;
                   1324:        default:
                   1325:                break;
                   1326:        }
                   1327: }
                   1328:
                   1329: static void
                   1330: termp_lb_post(DECL_ARGS)
                   1331: {
                   1332:
1.338     schwarze 1333:        if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
1.81      kristaps 1334:                term_newln(p);
1.1       kristaps 1335: }
                   1336:
                   1337: static int
                   1338: termp_d1_pre(DECL_ARGS)
                   1339: {
                   1340:
1.314     schwarze 1341:        if (n->type != ROFFT_BLOCK)
1.328     schwarze 1342:                return 1;
1.1       kristaps 1343:        term_newln(p);
1.362     schwarze 1344:        p->tcol->offset += term_len(p, p->defindent + 1);
1.356     schwarze 1345:        term_tab_set(p, NULL);
                   1346:        term_tab_set(p, "T");
                   1347:        term_tab_set(p, ".5i");
1.328     schwarze 1348:        return 1;
1.1       kristaps 1349: }
                   1350:
                   1351: static int
                   1352: termp_ft_pre(DECL_ARGS)
                   1353: {
                   1354:
1.338     schwarze 1355:        /* NB: NODE_LINE does not effect this! */
1.143     kristaps 1356:        synopsis_pre(p, n);
1.102     kristaps 1357:        term_fontpush(p, TERMFONT_UNDER);
1.328     schwarze 1358:        return 1;
1.1       kristaps 1359: }
                   1360:
                   1361: static int
                   1362: termp_fn_pre(DECL_ARGS)
                   1363: {
1.257     schwarze 1364:        size_t           rmargin = 0;
1.226     kristaps 1365:        int              pretty;
                   1366:
1.338     schwarze 1367:        pretty = NODE_SYNPRETTY & n->flags;
1.1       kristaps 1368:
1.143     kristaps 1369:        synopsis_pre(p, n);
1.139     kristaps 1370:
1.226     kristaps 1371:        if (NULL == (n = n->child))
1.328     schwarze 1372:                return 0;
1.226     kristaps 1373:
1.251     schwarze 1374:        if (pretty) {
1.362     schwarze 1375:                rmargin = p->tcol->rmargin;
                   1376:                p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1.263     schwarze 1377:                p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1.251     schwarze 1378:        }
                   1379:
1.314     schwarze 1380:        assert(n->type == ROFFT_TEXT);
1.102     kristaps 1381:        term_fontpush(p, TERMFONT_BOLD);
1.226     kristaps 1382:        term_word(p, n->string);
1.102     kristaps 1383:        term_fontpop(p);
1.1       kristaps 1384:
1.336     schwarze 1385:        if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
1.376   ! schwarze 1386:                tag_put(n->string, fn_prio++, p->line);
1.322     schwarze 1387:
1.251     schwarze 1388:        if (pretty) {
                   1389:                term_flushln(p);
1.263     schwarze 1390:                p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1.360     schwarze 1391:                p->flags |= TERMP_NOPAD;
1.362     schwarze 1392:                p->tcol->offset = p->tcol->rmargin;
                   1393:                p->tcol->rmargin = rmargin;
1.251     schwarze 1394:        }
                   1395:
1.1       kristaps 1396:        p->flags |= TERMP_NOSPACE;
                   1397:        term_word(p, "(");
1.222     kristaps 1398:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1399:
1.226     kristaps 1400:        for (n = n->next; n; n = n->next) {
1.314     schwarze 1401:                assert(n->type == ROFFT_TEXT);
1.102     kristaps 1402:                term_fontpush(p, TERMFONT_UNDER);
1.255     schwarze 1403:                if (pretty)
                   1404:                        p->flags |= TERMP_NBRWORD;
1.226     kristaps 1405:                term_word(p, n->string);
1.102     kristaps 1406:                term_fontpop(p);
                   1407:
1.226     kristaps 1408:                if (n->next) {
1.222     kristaps 1409:                        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1410:                        term_word(p, ",");
1.222     kristaps 1411:                }
1.1       kristaps 1412:        }
                   1413:
1.222     kristaps 1414:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1415:        term_word(p, ")");
                   1416:
1.226     kristaps 1417:        if (pretty) {
1.222     kristaps 1418:                p->flags |= TERMP_NOSPACE;
1.1       kristaps 1419:                term_word(p, ";");
1.251     schwarze 1420:                term_flushln(p);
1.222     kristaps 1421:        }
1.1       kristaps 1422:
1.328     schwarze 1423:        return 0;
1.1       kristaps 1424: }
                   1425:
                   1426: static int
                   1427: termp_fa_pre(DECL_ARGS)
                   1428: {
1.315     schwarze 1429:        const struct roff_node  *nn;
1.1       kristaps 1430:
1.86      kristaps 1431:        if (n->parent->tok != MDOC_Fo) {
1.102     kristaps 1432:                term_fontpush(p, TERMFONT_UNDER);
1.328     schwarze 1433:                return 1;
1.1       kristaps 1434:        }
                   1435:
1.86      kristaps 1436:        for (nn = n->child; nn; nn = nn->next) {
1.102     kristaps 1437:                term_fontpush(p, TERMFONT_UNDER);
1.258     schwarze 1438:                p->flags |= TERMP_NBRWORD;
1.86      kristaps 1439:                term_word(p, nn->string);
1.102     kristaps 1440:                term_fontpop(p);
                   1441:
1.256     schwarze 1442:                if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1.222     kristaps 1443:                        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1444:                        term_word(p, ",");
1.222     kristaps 1445:                }
1.1       kristaps 1446:        }
                   1447:
1.328     schwarze 1448:        return 0;
1.1       kristaps 1449: }
                   1450:
                   1451: static int
                   1452: termp_bd_pre(DECL_ARGS)
                   1453: {
1.302     schwarze 1454:        int                      offset;
1.1       kristaps 1455:
1.314     schwarze 1456:        if (n->type == ROFFT_BLOCK) {
1.151     kristaps 1457:                print_bvspace(p, n, n);
1.328     schwarze 1458:                return 1;
1.314     schwarze 1459:        } else if (n->type == ROFFT_HEAD)
1.328     schwarze 1460:                return 0;
1.1       kristaps 1461:
1.288     schwarze 1462:        /* Handle the -offset argument. */
                   1463:
                   1464:        if (n->norm->Bd.offs == NULL ||
                   1465:            ! strcmp(n->norm->Bd.offs, "left"))
                   1466:                /* nothing */;
                   1467:        else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1.362     schwarze 1468:                p->tcol->offset += term_len(p, p->defindent + 1);
1.288     schwarze 1469:        else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1.362     schwarze 1470:                p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
1.302     schwarze 1471:        else {
                   1472:                offset = a2width(p, n->norm->Bd.offs);
1.362     schwarze 1473:                if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
                   1474:                        p->tcol->offset = 0;
1.302     schwarze 1475:                else if (offset < SHRT_MAX)
1.362     schwarze 1476:                        p->tcol->offset += offset;
1.302     schwarze 1477:        }
1.85      kristaps 1478:
1.372     schwarze 1479:        switch (n->norm->Bd.type) {
                   1480:        case DISP_literal:
1.356     schwarze 1481:                term_tab_set(p, NULL);
                   1482:                term_tab_set(p, "T");
                   1483:                term_tab_set(p, "8n");
1.372     schwarze 1484:                break;
                   1485:        case DISP_centered:
                   1486:                p->flags |= TERMP_CENTER;
                   1487:                break;
                   1488:        default:
                   1489:                break;
1.356     schwarze 1490:        }
1.372     schwarze 1491:        return 1;
1.1       kristaps 1492: }
                   1493:
                   1494: static void
                   1495: termp_bd_post(DECL_ARGS)
                   1496: {
1.314     schwarze 1497:        if (n->type != ROFFT_BODY)
1.1       kristaps 1498:                return;
1.372     schwarze 1499:        if (n->norm->Bd.type == DISP_unfilled ||
                   1500:            n->norm->Bd.type == DISP_literal)
1.361     schwarze 1501:                p->flags |= TERMP_BRNEVER;
1.60      kristaps 1502:        p->flags |= TERMP_NOSPACE;
1.130     schwarze 1503:        term_newln(p);
1.361     schwarze 1504:        p->flags &= ~TERMP_BRNEVER;
1.372     schwarze 1505:        if (n->norm->Bd.type == DISP_centered)
                   1506:                p->flags &= ~TERMP_CENTER;
1.1       kristaps 1507: }
                   1508:
                   1509: static int
1.26      kristaps 1510: termp_xx_pre(DECL_ARGS)
1.1       kristaps 1511: {
1.339     schwarze 1512:        if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
                   1513:                p->flags |= TERMP_PREKEEP;
                   1514:        return 1;
                   1515: }
1.1       kristaps 1516:
1.339     schwarze 1517: static void
                   1518: termp_xx_post(DECL_ARGS)
                   1519: {
                   1520:        if (n->aux == 0)
                   1521:                p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1.1       kristaps 1522: }
                   1523:
                   1524: static void
                   1525: termp_pf_post(DECL_ARGS)
                   1526: {
                   1527:
1.338     schwarze 1528:        if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1.298     schwarze 1529:                p->flags |= TERMP_NOSPACE;
1.1       kristaps 1530: }
                   1531:
                   1532: static int
                   1533: termp_ss_pre(DECL_ARGS)
                   1534: {
1.337     schwarze 1535:        struct roff_node *nn;
1.1       kristaps 1536:
1.86      kristaps 1537:        switch (n->type) {
1.314     schwarze 1538:        case ROFFT_BLOCK:
1.1       kristaps 1539:                term_newln(p);
1.337     schwarze 1540:                for (nn = n->prev; nn != NULL; nn = nn->prev)
1.367     schwarze 1541:                        if (nn->type != ROFFT_COMMENT &&
                   1542:                            (nn->flags & NODE_NOPRT) == 0)
1.337     schwarze 1543:                                break;
                   1544:                if (nn != NULL)
1.1       kristaps 1545:                        term_vspace(p);
                   1546:                break;
1.314     schwarze 1547:        case ROFFT_HEAD:
1.102     kristaps 1548:                term_fontpush(p, TERMFONT_BOLD);
1.362     schwarze 1549:                p->tcol->offset = term_len(p, (p->defindent+1)/2);
1.1       kristaps 1550:                break;
1.314     schwarze 1551:        case ROFFT_BODY:
1.362     schwarze 1552:                p->tcol->offset = term_len(p, p->defindent);
1.356     schwarze 1553:                term_tab_set(p, NULL);
                   1554:                term_tab_set(p, "T");
                   1555:                term_tab_set(p, ".5i");
1.274     schwarze 1556:                break;
1.1       kristaps 1557:        default:
                   1558:                break;
                   1559:        }
                   1560:
1.328     schwarze 1561:        return 1;
1.1       kristaps 1562: }
                   1563:
                   1564: static void
                   1565: termp_ss_post(DECL_ARGS)
                   1566: {
                   1567:
1.314     schwarze 1568:        if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1.1       kristaps 1569:                term_newln(p);
                   1570: }
                   1571:
                   1572: static int
                   1573: termp_cd_pre(DECL_ARGS)
                   1574: {
                   1575:
1.143     kristaps 1576:        synopsis_pre(p, n);
1.102     kristaps 1577:        term_fontpush(p, TERMFONT_BOLD);
1.328     schwarze 1578:        return 1;
1.1       kristaps 1579: }
                   1580:
                   1581: static int
                   1582: termp_in_pre(DECL_ARGS)
                   1583: {
1.142     schwarze 1584:
1.143     kristaps 1585:        synopsis_pre(p, n);
1.1       kristaps 1586:
1.338     schwarze 1587:        if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
1.138     kristaps 1588:                term_fontpush(p, TERMFONT_BOLD);
1.22      kristaps 1589:                term_word(p, "#include");
1.138     kristaps 1590:                term_word(p, "<");
                   1591:        } else {
                   1592:                term_word(p, "<");
                   1593:                term_fontpush(p, TERMFONT_UNDER);
                   1594:        }
1.22      kristaps 1595:
1.1       kristaps 1596:        p->flags |= TERMP_NOSPACE;
1.328     schwarze 1597:        return 1;
1.1       kristaps 1598: }
                   1599:
                   1600: static void
                   1601: termp_in_post(DECL_ARGS)
                   1602: {
                   1603:
1.338     schwarze 1604:        if (NODE_SYNPRETTY & n->flags)
1.138     kristaps 1605:                term_fontpush(p, TERMFONT_BOLD);
                   1606:
1.79      kristaps 1607:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1608:        term_word(p, ">");
                   1609:
1.338     schwarze 1610:        if (NODE_SYNPRETTY & n->flags)
1.138     kristaps 1611:                term_fontpop(p);
1.1       kristaps 1612: }
                   1613:
                   1614: static int
1.355     schwarze 1615: termp_pp_pre(DECL_ARGS)
1.45      kristaps 1616: {
1.376   ! schwarze 1617:        fn_prio = TAG_STRONG;
1.355     schwarze 1618:        term_vspace(p);
1.328     schwarze 1619:        return 0;
1.45      kristaps 1620: }
                   1621:
                   1622: static int
1.294     schwarze 1623: termp_skip_pre(DECL_ARGS)
1.268     schwarze 1624: {
                   1625:
1.328     schwarze 1626:        return 0;
1.268     schwarze 1627: }
                   1628:
                   1629: static int
1.188     kristaps 1630: termp_quote_pre(DECL_ARGS)
1.1       kristaps 1631: {
                   1632:
1.314     schwarze 1633:        if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1.328     schwarze 1634:                return 1;
1.1       kristaps 1635:
1.188     kristaps 1636:        switch (n->tok) {
1.264     schwarze 1637:        case MDOC_Ao:
                   1638:        case MDOC_Aq:
1.331     schwarze 1639:                term_word(p, n->child != NULL && n->child->next == NULL &&
1.303     schwarze 1640:                    n->child->tok == MDOC_Mt ? "<" : "\\(la");
1.188     kristaps 1641:                break;
1.264     schwarze 1642:        case MDOC_Bro:
                   1643:        case MDOC_Brq:
1.188     kristaps 1644:                term_word(p, "{");
                   1645:                break;
1.264     schwarze 1646:        case MDOC_Oo:
                   1647:        case MDOC_Op:
                   1648:        case MDOC_Bo:
                   1649:        case MDOC_Bq:
1.188     kristaps 1650:                term_word(p, "[");
                   1651:                break;
1.346     schwarze 1652:        case MDOC__T:
                   1653:                /* FALLTHROUGH */
1.264     schwarze 1654:        case MDOC_Do:
                   1655:        case MDOC_Dq:
1.366     schwarze 1656:                term_word(p, "\\(lq");
1.188     kristaps 1657:                break;
1.268     schwarze 1658:        case MDOC_En:
                   1659:                if (NULL == n->norm->Es ||
                   1660:                    NULL == n->norm->Es->child)
1.328     schwarze 1661:                        return 1;
1.268     schwarze 1662:                term_word(p, n->norm->Es->child->string);
                   1663:                break;
1.264     schwarze 1664:        case MDOC_Po:
                   1665:        case MDOC_Pq:
1.188     kristaps 1666:                term_word(p, "(");
                   1667:                break;
1.264     schwarze 1668:        case MDOC_Qo:
                   1669:        case MDOC_Qq:
1.188     kristaps 1670:                term_word(p, "\"");
                   1671:                break;
1.264     schwarze 1672:        case MDOC_Ql:
                   1673:        case MDOC_So:
                   1674:        case MDOC_Sq:
1.249     schwarze 1675:                term_word(p, "\\(oq");
1.188     kristaps 1676:                break;
                   1677:        default:
                   1678:                abort();
                   1679:        }
1.1       kristaps 1680:
                   1681:        p->flags |= TERMP_NOSPACE;
1.328     schwarze 1682:        return 1;
1.1       kristaps 1683: }
                   1684:
                   1685: static void
1.188     kristaps 1686: termp_quote_post(DECL_ARGS)
1.1       kristaps 1687: {
                   1688:
1.314     schwarze 1689:        if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1.1       kristaps 1690:                return;
                   1691:
1.306     schwarze 1692:        p->flags |= TERMP_NOSPACE;
1.1       kristaps 1693:
1.188     kristaps 1694:        switch (n->tok) {
1.264     schwarze 1695:        case MDOC_Ao:
                   1696:        case MDOC_Aq:
1.331     schwarze 1697:                term_word(p, n->child != NULL && n->child->next == NULL &&
1.303     schwarze 1698:                    n->child->tok == MDOC_Mt ? ">" : "\\(ra");
1.188     kristaps 1699:                break;
1.264     schwarze 1700:        case MDOC_Bro:
                   1701:        case MDOC_Brq:
1.188     kristaps 1702:                term_word(p, "}");
                   1703:                break;
1.264     schwarze 1704:        case MDOC_Oo:
                   1705:        case MDOC_Op:
                   1706:        case MDOC_Bo:
                   1707:        case MDOC_Bq:
1.188     kristaps 1708:                term_word(p, "]");
                   1709:                break;
1.346     schwarze 1710:        case MDOC__T:
                   1711:                /* FALLTHROUGH */
1.264     schwarze 1712:        case MDOC_Do:
                   1713:        case MDOC_Dq:
1.366     schwarze 1714:                term_word(p, "\\(rq");
1.268     schwarze 1715:                break;
                   1716:        case MDOC_En:
1.306     schwarze 1717:                if (n->norm->Es == NULL ||
                   1718:                    n->norm->Es->child == NULL ||
                   1719:                    n->norm->Es->child->next == NULL)
                   1720:                        p->flags &= ~TERMP_NOSPACE;
                   1721:                else
1.268     schwarze 1722:                        term_word(p, n->norm->Es->child->next->string);
1.188     kristaps 1723:                break;
1.264     schwarze 1724:        case MDOC_Po:
                   1725:        case MDOC_Pq:
1.188     kristaps 1726:                term_word(p, ")");
                   1727:                break;
1.264     schwarze 1728:        case MDOC_Qo:
                   1729:        case MDOC_Qq:
1.188     kristaps 1730:                term_word(p, "\"");
                   1731:                break;
1.264     schwarze 1732:        case MDOC_Ql:
                   1733:        case MDOC_So:
                   1734:        case MDOC_Sq:
1.249     schwarze 1735:                term_word(p, "\\(cq");
1.188     kristaps 1736:                break;
                   1737:        default:
                   1738:                abort();
                   1739:        }
1.306     schwarze 1740: }
                   1741:
                   1742: static int
                   1743: termp_eo_pre(DECL_ARGS)
                   1744: {
                   1745:
1.314     schwarze 1746:        if (n->type != ROFFT_BODY)
1.328     schwarze 1747:                return 1;
1.306     schwarze 1748:
                   1749:        if (n->end == ENDBODY_NOT &&
                   1750:            n->parent->head->child == NULL &&
                   1751:            n->child != NULL &&
                   1752:            n->child->end != ENDBODY_NOT)
                   1753:                term_word(p, "\\&");
                   1754:        else if (n->end != ENDBODY_NOT ? n->child != NULL :
1.309     schwarze 1755:             n->parent->head->child != NULL && (n->child != NULL ||
                   1756:             (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1.306     schwarze 1757:                p->flags |= TERMP_NOSPACE;
                   1758:
1.328     schwarze 1759:        return 1;
1.306     schwarze 1760: }
                   1761:
                   1762: static void
                   1763: termp_eo_post(DECL_ARGS)
                   1764: {
                   1765:        int      body, tail;
                   1766:
1.314     schwarze 1767:        if (n->type != ROFFT_BODY)
1.306     schwarze 1768:                return;
                   1769:
                   1770:        if (n->end != ENDBODY_NOT) {
                   1771:                p->flags &= ~TERMP_NOSPACE;
                   1772:                return;
                   1773:        }
                   1774:
                   1775:        body = n->child != NULL || n->parent->head->child != NULL;
                   1776:        tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
                   1777:
                   1778:        if (body && tail)
                   1779:                p->flags |= TERMP_NOSPACE;
                   1780:        else if ( ! (body || tail))
                   1781:                term_word(p, "\\&");
                   1782:        else if ( ! tail)
                   1783:                p->flags &= ~TERMP_NOSPACE;
1.1       kristaps 1784: }
                   1785:
                   1786: static int
                   1787: termp_fo_pre(DECL_ARGS)
                   1788: {
1.257     schwarze 1789:        size_t           rmargin = 0;
1.256     schwarze 1790:        int              pretty;
                   1791:
1.338     schwarze 1792:        pretty = NODE_SYNPRETTY & n->flags;
1.1       kristaps 1793:
1.314     schwarze 1794:        if (n->type == ROFFT_BLOCK) {
1.143     kristaps 1795:                synopsis_pre(p, n);
1.328     schwarze 1796:                return 1;
1.314     schwarze 1797:        } else if (n->type == ROFFT_BODY) {
1.256     schwarze 1798:                if (pretty) {
1.362     schwarze 1799:                        rmargin = p->tcol->rmargin;
                   1800:                        p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1.263     schwarze 1801:                        p->flags |= TERMP_NOBREAK | TERMP_BRIND |
                   1802:                                        TERMP_HANG;
1.256     schwarze 1803:                }
1.33      kristaps 1804:                p->flags |= TERMP_NOSPACE;
1.1       kristaps 1805:                term_word(p, "(");
1.222     kristaps 1806:                p->flags |= TERMP_NOSPACE;
1.256     schwarze 1807:                if (pretty) {
                   1808:                        term_flushln(p);
1.263     schwarze 1809:                        p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
                   1810:                                        TERMP_HANG);
1.360     schwarze 1811:                        p->flags |= TERMP_NOPAD;
1.362     schwarze 1812:                        p->tcol->offset = p->tcol->rmargin;
                   1813:                        p->tcol->rmargin = rmargin;
1.256     schwarze 1814:                }
1.328     schwarze 1815:                return 1;
1.256     schwarze 1816:        }
1.141     kristaps 1817:
1.169     kristaps 1818:        if (NULL == n->child)
1.328     schwarze 1819:                return 0;
1.169     kristaps 1820:
1.141     kristaps 1821:        /* XXX: we drop non-initial arguments as per groff. */
1.1       kristaps 1822:
1.141     kristaps 1823:        assert(n->child->string);
1.102     kristaps 1824:        term_fontpush(p, TERMFONT_BOLD);
1.141     kristaps 1825:        term_word(p, n->child->string);
1.328     schwarze 1826:        return 0;
1.1       kristaps 1827: }
                   1828:
                   1829: static void
                   1830: termp_fo_post(DECL_ARGS)
                   1831: {
                   1832:
1.314     schwarze 1833:        if (n->type != ROFFT_BODY)
1.143     kristaps 1834:                return;
                   1835:
1.222     kristaps 1836:        p->flags |= TERMP_NOSPACE;
1.143     kristaps 1837:        term_word(p, ")");
                   1838:
1.338     schwarze 1839:        if (NODE_SYNPRETTY & n->flags) {
1.222     kristaps 1840:                p->flags |= TERMP_NOSPACE;
1.143     kristaps 1841:                term_word(p, ";");
1.256     schwarze 1842:                term_flushln(p);
1.222     kristaps 1843:        }
1.1       kristaps 1844: }
                   1845:
                   1846: static int
                   1847: termp_bf_pre(DECL_ARGS)
                   1848: {
                   1849:
1.314     schwarze 1850:        if (n->type == ROFFT_HEAD)
1.328     schwarze 1851:                return 0;
1.314     schwarze 1852:        else if (n->type != ROFFT_BODY)
1.328     schwarze 1853:                return 1;
1.1       kristaps 1854:
1.264     schwarze 1855:        if (FONT_Em == n->norm->Bf.font)
1.102     kristaps 1856:                term_fontpush(p, TERMFONT_UNDER);
1.264     schwarze 1857:        else if (FONT_Sy == n->norm->Bf.font)
1.102     kristaps 1858:                term_fontpush(p, TERMFONT_BOLD);
1.264     schwarze 1859:        else
1.102     kristaps 1860:                term_fontpush(p, TERMFONT_NONE);
1.1       kristaps 1861:
1.328     schwarze 1862:        return 1;
1.1       kristaps 1863: }
                   1864:
                   1865: static int
                   1866: termp_sm_pre(DECL_ARGS)
                   1867: {
                   1868:
1.269     schwarze 1869:        if (NULL == n->child)
                   1870:                p->flags ^= TERMP_NONOSPACE;
                   1871:        else if (0 == strcmp("on", n->child->string))
1.1       kristaps 1872:                p->flags &= ~TERMP_NONOSPACE;
1.269     schwarze 1873:        else
1.1       kristaps 1874:                p->flags |= TERMP_NONOSPACE;
1.269     schwarze 1875:
                   1876:        if (p->col && ! (TERMP_NONOSPACE & p->flags))
                   1877:                p->flags &= ~TERMP_NOSPACE;
1.1       kristaps 1878:
1.328     schwarze 1879:        return 0;
1.1       kristaps 1880: }
                   1881:
                   1882: static int
                   1883: termp_ap_pre(DECL_ARGS)
                   1884: {
                   1885:
                   1886:        p->flags |= TERMP_NOSPACE;
1.188     kristaps 1887:        term_word(p, "'");
1.1       kristaps 1888:        p->flags |= TERMP_NOSPACE;
1.328     schwarze 1889:        return 1;
1.1       kristaps 1890: }
                   1891:
                   1892: static void
                   1893: termp____post(DECL_ARGS)
                   1894: {
1.184     kristaps 1895:
                   1896:        /*
                   1897:         * Handle lists of authors.  In general, print each followed by
                   1898:         * a comma.  Don't print the comma if there are only two
                   1899:         * authors.
                   1900:         */
                   1901:        if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
                   1902:                if (NULL == n->next->next || MDOC__A != n->next->next->tok)
                   1903:                        if (NULL == n->prev || MDOC__A != n->prev->tok)
                   1904:                                return;
1.1       kristaps 1905:
1.95      kristaps 1906:        /* TODO: %U. */
                   1907:
1.187     kristaps 1908:        if (NULL == n->parent || MDOC_Rs != n->parent->tok)
                   1909:                return;
                   1910:
1.222     kristaps 1911:        p->flags |= TERMP_NOSPACE;
1.186     kristaps 1912:        if (NULL == n->next) {
                   1913:                term_word(p, ".");
                   1914:                p->flags |= TERMP_SENTENCE;
                   1915:        } else
                   1916:                term_word(p, ",");
1.1       kristaps 1917: }
                   1918:
                   1919: static int
1.102     kristaps 1920: termp_li_pre(DECL_ARGS)
                   1921: {
                   1922:
1.335     schwarze 1923:        termp_tag_pre(p, pair, meta, n);
1.102     kristaps 1924:        term_fontpush(p, TERMFONT_NONE);
1.328     schwarze 1925:        return 1;
1.102     kristaps 1926: }
                   1927:
                   1928: static int
1.1       kristaps 1929: termp_lk_pre(DECL_ARGS)
                   1930: {
1.358     schwarze 1931:        const struct roff_node *link, *descr, *punct;
1.1       kristaps 1932:
1.349     schwarze 1933:        if ((link = n->child) == NULL)
1.328     schwarze 1934:                return 0;
1.181     kristaps 1935:
1.358     schwarze 1936:        /* Find beginning of trailing punctuation. */
                   1937:        punct = n->last;
                   1938:        while (punct != link && punct->flags & NODE_DELIMC)
                   1939:                punct = punct->prev;
                   1940:        punct = punct->next;
                   1941:
1.349     schwarze 1942:        /* Link text. */
1.358     schwarze 1943:        if ((descr = link->next) != NULL && descr != punct) {
1.240     schwarze 1944:                term_fontpush(p, TERMFONT_UNDER);
1.358     schwarze 1945:                while (descr != punct) {
                   1946:                        if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
                   1947:                                p->flags |= TERMP_NOSPACE;
1.240     schwarze 1948:                        term_word(p, descr->string);
                   1949:                        descr = descr->next;
                   1950:                }
1.347     schwarze 1951:                term_fontpop(p);
1.240     schwarze 1952:                p->flags |= TERMP_NOSPACE;
                   1953:                term_word(p, ":");
                   1954:        }
1.1       kristaps 1955:
1.349     schwarze 1956:        /* Link target. */
1.102     kristaps 1957:        term_fontpush(p, TERMFONT_BOLD);
1.240     schwarze 1958:        term_word(p, link->string);
1.102     kristaps 1959:        term_fontpop(p);
1.348     schwarze 1960:
1.349     schwarze 1961:        /* Trailing punctuation. */
1.358     schwarze 1962:        while (punct != NULL) {
1.349     schwarze 1963:                p->flags |= TERMP_NOSPACE;
1.358     schwarze 1964:                term_word(p, punct->string);
                   1965:                punct = punct->next;
1.349     schwarze 1966:        }
1.328     schwarze 1967:        return 0;
1.1       kristaps 1968: }
                   1969:
1.159     schwarze 1970: static int
                   1971: termp_bk_pre(DECL_ARGS)
                   1972: {
                   1973:
1.161     schwarze 1974:        switch (n->type) {
1.314     schwarze 1975:        case ROFFT_BLOCK:
1.166     kristaps 1976:                break;
1.314     schwarze 1977:        case ROFFT_HEAD:
1.328     schwarze 1978:                return 0;
1.314     schwarze 1979:        case ROFFT_BODY:
1.331     schwarze 1980:                if (n->parent->args != NULL || n->prev->child == NULL)
1.200     schwarze 1981:                        p->flags |= TERMP_PREKEEP;
1.166     kristaps 1982:                break;
1.161     schwarze 1983:        default:
                   1984:                abort();
                   1985:        }
1.166     kristaps 1986:
1.328     schwarze 1987:        return 1;
1.159     schwarze 1988: }
                   1989:
                   1990: static void
                   1991: termp_bk_post(DECL_ARGS)
                   1992: {
                   1993:
1.314     schwarze 1994:        if (n->type == ROFFT_BODY)
1.161     schwarze 1995:                p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1.203     kristaps 1996: }
                   1997:
                   1998: static void
                   1999: termp__t_post(DECL_ARGS)
                   2000: {
                   2001:
                   2002:        /*
                   2003:         * If we're in an `Rs' and there's a journal present, then quote
                   2004:         * us instead of underlining us (for disambiguation).
                   2005:         */
1.217     schwarze 2006:        if (n->parent && MDOC_Rs == n->parent->tok &&
1.264     schwarze 2007:            n->parent->norm->Rs.quote_T)
1.245     schwarze 2008:                termp_quote_post(p, pair, meta, n);
1.203     kristaps 2009:
1.245     schwarze 2010:        termp____post(p, pair, meta, n);
1.203     kristaps 2011: }
                   2012:
                   2013: static int
                   2014: termp__t_pre(DECL_ARGS)
                   2015: {
                   2016:
                   2017:        /*
                   2018:         * If we're in an `Rs' and there's a journal present, then quote
                   2019:         * us instead of underlining us (for disambiguation).
                   2020:         */
                   2021:        if (n->parent && MDOC_Rs == n->parent->tok &&
1.264     schwarze 2022:            n->parent->norm->Rs.quote_T)
1.328     schwarze 2023:                return termp_quote_pre(p, pair, meta, n);
1.203     kristaps 2024:
                   2025:        term_fontpush(p, TERMFONT_UNDER);
1.328     schwarze 2026:        return 1;
1.159     schwarze 2027: }
1.1       kristaps 2028:
                   2029: static int
1.69      kristaps 2030: termp_under_pre(DECL_ARGS)
1.1       kristaps 2031: {
                   2032:
1.102     kristaps 2033:        term_fontpush(p, TERMFONT_UNDER);
1.334     schwarze 2034:        return 1;
                   2035: }
                   2036:
                   2037: static int
                   2038: termp_em_pre(DECL_ARGS)
                   2039: {
                   2040:        if (n->child != NULL &&
                   2041:            n->child->type == ROFFT_TEXT)
1.376   ! schwarze 2042:                tag_put(n->child->string, TAG_FALLBACK, p->line);
1.334     schwarze 2043:        term_fontpush(p, TERMFONT_UNDER);
                   2044:        return 1;
                   2045: }
                   2046:
                   2047: static int
                   2048: termp_sy_pre(DECL_ARGS)
                   2049: {
                   2050:        if (n->child != NULL &&
                   2051:            n->child->type == ROFFT_TEXT)
1.376   ! schwarze 2052:                tag_put(n->child->string, TAG_FALLBACK, p->line);
1.334     schwarze 2053:        term_fontpush(p, TERMFONT_BOLD);
1.328     schwarze 2054:        return 1;
1.323     schwarze 2055: }
                   2056:
                   2057: static int
                   2058: termp_er_pre(DECL_ARGS)
                   2059: {
                   2060:
                   2061:        if (n->sec == SEC_ERRORS &&
                   2062:            (n->parent->tok == MDOC_It ||
                   2063:             (n->parent->tok == MDOC_Bq &&
1.324     schwarze 2064:              n->parent->parent->parent->tok == MDOC_It)))
1.376   ! schwarze 2065:                tag_put(n->child->string, TAG_STRONG, p->line);
1.328     schwarze 2066:        return 1;
1.320     schwarze 2067: }
                   2068:
                   2069: static int
                   2070: termp_tag_pre(DECL_ARGS)
                   2071: {
                   2072:
                   2073:        if (n->child != NULL &&
                   2074:            n->child->type == ROFFT_TEXT &&
1.333     schwarze 2075:            (n->prev == NULL ||
                   2076:             (n->prev->type == ROFFT_TEXT &&
                   2077:              strcmp(n->prev->string, "|") == 0)) &&
1.320     schwarze 2078:            (n->parent->tok == MDOC_It ||
                   2079:             (n->parent->tok == MDOC_Xo &&
                   2080:              n->parent->parent->prev == NULL &&
1.324     schwarze 2081:              n->parent->parent->parent->tok == MDOC_It)))
1.376   ! schwarze 2082:                tag_put(n->child->string, TAG_STRONG, p->line);
1.328     schwarze 2083:        return 1;
1.375     schwarze 2084: }
                   2085:
                   2086: static int
                   2087: termp_tg_pre(DECL_ARGS)
                   2088: {
1.376   ! schwarze 2089:        tag_put(n->child->string, TAG_MANUAL, p->line);
1.375     schwarze 2090:        return 0;
1.369     schwarze 2091: }
                   2092:
                   2093: static int
                   2094: termp_abort_pre(DECL_ARGS)
                   2095: {
                   2096:        abort();
1.84      kristaps 2097: }

CVSweb