1*99db7d0eSSascha Wildner /* $Id: mdoc_term.c,v 1.380 2020/04/06 10:16:17 schwarze Exp $ */
280387638SSascha Wildner /*
3*99db7d0eSSascha Wildner * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
460e1e752SSascha Wildner * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
57888c61dSFranco Fichtner * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
680387638SSascha Wildner *
780387638SSascha Wildner * Permission to use, copy, modify, and distribute this software for any
880387638SSascha Wildner * purpose with or without fee is hereby granted, provided that the above
980387638SSascha Wildner * copyright notice and this permission notice appear in all copies.
1080387638SSascha Wildner *
1154ba9607SSascha Wildner * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1280387638SSascha Wildner * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1354ba9607SSascha Wildner * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1480387638SSascha Wildner * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1580387638SSascha Wildner * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1680387638SSascha Wildner * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1780387638SSascha Wildner * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*99db7d0eSSascha Wildner *
19*99db7d0eSSascha Wildner * Plain text formatter for mdoc(7), used by mandoc(1)
20*99db7d0eSSascha Wildner * for ASCII, UTF-8, PostScript, and PDF output.
2180387638SSascha Wildner */
2280387638SSascha Wildner #include "config.h"
2380387638SSascha Wildner
2480387638SSascha Wildner #include <sys/types.h>
2580387638SSascha Wildner
2680387638SSascha Wildner #include <assert.h>
2780387638SSascha Wildner #include <ctype.h>
2854ba9607SSascha Wildner #include <limits.h>
2980387638SSascha Wildner #include <stdint.h>
3080387638SSascha Wildner #include <stdio.h>
3180387638SSascha Wildner #include <stdlib.h>
3280387638SSascha Wildner #include <string.h>
3380387638SSascha Wildner
34070c62a6SFranco Fichtner #include "mandoc_aux.h"
3554ba9607SSascha Wildner #include "roff.h"
3654ba9607SSascha Wildner #include "mdoc.h"
3780387638SSascha Wildner #include "out.h"
3880387638SSascha Wildner #include "term.h"
39*99db7d0eSSascha Wildner #include "term_tag.h"
4080387638SSascha Wildner #include "main.h"
4180387638SSascha Wildner
4280387638SSascha Wildner struct termpair {
4380387638SSascha Wildner struct termpair *ppair;
4480387638SSascha Wildner int count;
4580387638SSascha Wildner };
4680387638SSascha Wildner
4780387638SSascha Wildner #define DECL_ARGS struct termp *p, \
4880387638SSascha Wildner struct termpair *pair, \
4954ba9607SSascha Wildner const struct roff_meta *meta, \
5054ba9607SSascha Wildner struct roff_node *n
5180387638SSascha Wildner
5254ba9607SSascha Wildner struct mdoc_term_act {
5380387638SSascha Wildner int (*pre)(DECL_ARGS);
5480387638SSascha Wildner void (*post)(DECL_ARGS);
5580387638SSascha Wildner };
5680387638SSascha Wildner
5754ba9607SSascha Wildner static int a2width(const struct termp *, const char *);
5880387638SSascha Wildner
5980387638SSascha Wildner static void print_bvspace(struct termp *,
60*99db7d0eSSascha Wildner struct roff_node *, struct roff_node *);
6180387638SSascha Wildner static void print_mdoc_node(DECL_ARGS);
6280387638SSascha Wildner static void print_mdoc_nodelist(DECL_ARGS);
6354ba9607SSascha Wildner static void print_mdoc_head(struct termp *, const struct roff_meta *);
6454ba9607SSascha Wildner static void print_mdoc_foot(struct termp *, const struct roff_meta *);
65*99db7d0eSSascha Wildner static void synopsis_pre(struct termp *, struct roff_node *);
6680387638SSascha Wildner
6780387638SSascha Wildner static void termp____post(DECL_ARGS);
6880387638SSascha Wildner static void termp__t_post(DECL_ARGS);
6980387638SSascha Wildner static void termp_bd_post(DECL_ARGS);
7080387638SSascha Wildner static void termp_bk_post(DECL_ARGS);
7180387638SSascha Wildner static void termp_bl_post(DECL_ARGS);
7254ba9607SSascha Wildner static void termp_eo_post(DECL_ARGS);
73f88b6c16SFranco Fichtner static void termp_fd_post(DECL_ARGS);
7480387638SSascha Wildner static void termp_fo_post(DECL_ARGS);
7580387638SSascha Wildner static void termp_in_post(DECL_ARGS);
7680387638SSascha Wildner static void termp_it_post(DECL_ARGS);
7780387638SSascha Wildner static void termp_lb_post(DECL_ARGS);
7880387638SSascha Wildner static void termp_nm_post(DECL_ARGS);
7980387638SSascha Wildner static void termp_pf_post(DECL_ARGS);
8080387638SSascha Wildner static void termp_quote_post(DECL_ARGS);
8180387638SSascha Wildner static void termp_sh_post(DECL_ARGS);
8280387638SSascha Wildner static void termp_ss_post(DECL_ARGS);
8354ba9607SSascha Wildner static void termp_xx_post(DECL_ARGS);
8480387638SSascha Wildner
8580387638SSascha Wildner static int termp__a_pre(DECL_ARGS);
8680387638SSascha Wildner static int termp__t_pre(DECL_ARGS);
8754ba9607SSascha Wildner static int termp_abort_pre(DECL_ARGS);
8880387638SSascha Wildner static int termp_an_pre(DECL_ARGS);
8980387638SSascha Wildner static int termp_ap_pre(DECL_ARGS);
9080387638SSascha Wildner static int termp_bd_pre(DECL_ARGS);
9180387638SSascha Wildner static int termp_bf_pre(DECL_ARGS);
9280387638SSascha Wildner static int termp_bk_pre(DECL_ARGS);
9380387638SSascha Wildner static int termp_bl_pre(DECL_ARGS);
9480387638SSascha Wildner static int termp_bold_pre(DECL_ARGS);
9580387638SSascha Wildner static int termp_d1_pre(DECL_ARGS);
9654ba9607SSascha Wildner static int termp_eo_pre(DECL_ARGS);
9780387638SSascha Wildner static int termp_ex_pre(DECL_ARGS);
9880387638SSascha Wildner static int termp_fa_pre(DECL_ARGS);
9980387638SSascha Wildner static int termp_fd_pre(DECL_ARGS);
10080387638SSascha Wildner static int termp_fl_pre(DECL_ARGS);
10180387638SSascha Wildner static int termp_fn_pre(DECL_ARGS);
10280387638SSascha Wildner static int termp_fo_pre(DECL_ARGS);
10380387638SSascha Wildner static int termp_ft_pre(DECL_ARGS);
10480387638SSascha Wildner static int termp_in_pre(DECL_ARGS);
10580387638SSascha Wildner static int termp_it_pre(DECL_ARGS);
10680387638SSascha Wildner static int termp_li_pre(DECL_ARGS);
10780387638SSascha Wildner static int termp_lk_pre(DECL_ARGS);
10880387638SSascha Wildner static int termp_nd_pre(DECL_ARGS);
10980387638SSascha Wildner static int termp_nm_pre(DECL_ARGS);
11080387638SSascha Wildner static int termp_ns_pre(DECL_ARGS);
11180387638SSascha Wildner static int termp_quote_pre(DECL_ARGS);
11280387638SSascha Wildner static int termp_rs_pre(DECL_ARGS);
11380387638SSascha Wildner static int termp_sh_pre(DECL_ARGS);
11454ba9607SSascha Wildner static int termp_skip_pre(DECL_ARGS);
11580387638SSascha Wildner static int termp_sm_pre(DECL_ARGS);
11654ba9607SSascha Wildner static int termp_pp_pre(DECL_ARGS);
11780387638SSascha Wildner static int termp_ss_pre(DECL_ARGS);
11880387638SSascha Wildner static int termp_under_pre(DECL_ARGS);
11980387638SSascha Wildner static int termp_vt_pre(DECL_ARGS);
12080387638SSascha Wildner static int termp_xr_pre(DECL_ARGS);
12180387638SSascha Wildner static int termp_xx_pre(DECL_ARGS);
12280387638SSascha Wildner
12354ba9607SSascha Wildner static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
12480387638SSascha Wildner { NULL, NULL }, /* Dd */
12580387638SSascha Wildner { NULL, NULL }, /* Dt */
12680387638SSascha Wildner { NULL, NULL }, /* Os */
12780387638SSascha Wildner { termp_sh_pre, termp_sh_post }, /* Sh */
12880387638SSascha Wildner { termp_ss_pre, termp_ss_post }, /* Ss */
12954ba9607SSascha Wildner { termp_pp_pre, NULL }, /* Pp */
130f88b6c16SFranco Fichtner { termp_d1_pre, termp_bl_post }, /* D1 */
131f88b6c16SFranco Fichtner { termp_d1_pre, termp_bl_post }, /* Dl */
13280387638SSascha Wildner { termp_bd_pre, termp_bd_post }, /* Bd */
13380387638SSascha Wildner { NULL, NULL }, /* Ed */
13480387638SSascha Wildner { termp_bl_pre, termp_bl_post }, /* Bl */
13580387638SSascha Wildner { NULL, NULL }, /* El */
13680387638SSascha Wildner { termp_it_pre, termp_it_post }, /* It */
13780387638SSascha Wildner { termp_under_pre, NULL }, /* Ad */
13854ba9607SSascha Wildner { termp_an_pre, NULL }, /* An */
13954ba9607SSascha Wildner { termp_ap_pre, NULL }, /* Ap */
14080387638SSascha Wildner { termp_under_pre, NULL }, /* Ar */
141*99db7d0eSSascha Wildner { termp_fd_pre, NULL }, /* Cd */
14280387638SSascha Wildner { termp_bold_pre, NULL }, /* Cm */
14354ba9607SSascha Wildner { termp_li_pre, NULL }, /* Dv */
144*99db7d0eSSascha Wildner { NULL, NULL }, /* Er */
145*99db7d0eSSascha Wildner { NULL, NULL }, /* Ev */
14680387638SSascha Wildner { termp_ex_pre, NULL }, /* Ex */
14780387638SSascha Wildner { termp_fa_pre, NULL }, /* Fa */
148f88b6c16SFranco Fichtner { termp_fd_pre, termp_fd_post }, /* Fd */
14980387638SSascha Wildner { termp_fl_pre, NULL }, /* Fl */
15080387638SSascha Wildner { termp_fn_pre, NULL }, /* Fn */
15180387638SSascha Wildner { termp_ft_pre, NULL }, /* Ft */
15280387638SSascha Wildner { termp_bold_pre, NULL }, /* Ic */
15380387638SSascha Wildner { termp_in_pre, termp_in_post }, /* In */
15480387638SSascha Wildner { termp_li_pre, NULL }, /* Li */
15580387638SSascha Wildner { termp_nd_pre, NULL }, /* Nd */
15680387638SSascha Wildner { termp_nm_pre, termp_nm_post }, /* Nm */
15780387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Op */
15854ba9607SSascha Wildner { termp_abort_pre, NULL }, /* Ot */
15980387638SSascha Wildner { termp_under_pre, NULL }, /* Pa */
16054ba9607SSascha Wildner { termp_ex_pre, NULL }, /* Rv */
16180387638SSascha Wildner { NULL, NULL }, /* St */
16280387638SSascha Wildner { termp_under_pre, NULL }, /* Va */
16380387638SSascha Wildner { termp_vt_pre, NULL }, /* Vt */
16480387638SSascha Wildner { termp_xr_pre, NULL }, /* Xr */
16580387638SSascha Wildner { termp__a_pre, termp____post }, /* %A */
16680387638SSascha Wildner { termp_under_pre, termp____post }, /* %B */
16780387638SSascha Wildner { NULL, termp____post }, /* %D */
16880387638SSascha Wildner { termp_under_pre, termp____post }, /* %I */
16980387638SSascha Wildner { termp_under_pre, termp____post }, /* %J */
17080387638SSascha Wildner { NULL, termp____post }, /* %N */
17180387638SSascha Wildner { NULL, termp____post }, /* %O */
17280387638SSascha Wildner { NULL, termp____post }, /* %P */
17380387638SSascha Wildner { NULL, termp____post }, /* %R */
17480387638SSascha Wildner { termp__t_pre, termp__t_post }, /* %T */
17580387638SSascha Wildner { NULL, termp____post }, /* %V */
17680387638SSascha Wildner { NULL, NULL }, /* Ac */
17780387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Ao */
17880387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Aq */
17980387638SSascha Wildner { NULL, NULL }, /* At */
18080387638SSascha Wildner { NULL, NULL }, /* Bc */
18180387638SSascha Wildner { termp_bf_pre, NULL }, /* Bf */
18280387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Bo */
18380387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Bq */
18454ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Bsx */
18554ba9607SSascha Wildner { NULL, NULL }, /* Bx */
18654ba9607SSascha Wildner { termp_skip_pre, NULL }, /* Db */
18780387638SSascha Wildner { NULL, NULL }, /* Dc */
18880387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Do */
18980387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Dq */
19080387638SSascha Wildner { NULL, NULL }, /* Ec */ /* FIXME: no space */
19180387638SSascha Wildner { NULL, NULL }, /* Ef */
192*99db7d0eSSascha Wildner { termp_under_pre, NULL }, /* Em */
19354ba9607SSascha Wildner { termp_eo_pre, termp_eo_post }, /* Eo */
19454ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Fx */
19580387638SSascha Wildner { termp_bold_pre, NULL }, /* Ms */
19654ba9607SSascha Wildner { termp_li_pre, NULL }, /* No */
19780387638SSascha Wildner { termp_ns_pre, NULL }, /* Ns */
19854ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Nx */
19954ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Ox */
20080387638SSascha Wildner { NULL, NULL }, /* Pc */
2017888c61dSFranco Fichtner { NULL, termp_pf_post }, /* Pf */
20280387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Po */
20380387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Pq */
20480387638SSascha Wildner { NULL, NULL }, /* Qc */
20580387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Ql */
20680387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Qo */
20780387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Qq */
20880387638SSascha Wildner { NULL, NULL }, /* Re */
20980387638SSascha Wildner { termp_rs_pre, NULL }, /* Rs */
21080387638SSascha Wildner { NULL, NULL }, /* Sc */
21180387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* So */
21280387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Sq */
21380387638SSascha Wildner { termp_sm_pre, NULL }, /* Sm */
21480387638SSascha Wildner { termp_under_pre, NULL }, /* Sx */
215*99db7d0eSSascha Wildner { termp_bold_pre, NULL }, /* Sy */
21680387638SSascha Wildner { NULL, NULL }, /* Tn */
21754ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Ux */
21880387638SSascha Wildner { NULL, NULL }, /* Xc */
21980387638SSascha Wildner { NULL, NULL }, /* Xo */
22080387638SSascha Wildner { termp_fo_pre, termp_fo_post }, /* Fo */
22180387638SSascha Wildner { NULL, NULL }, /* Fc */
22280387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Oo */
22380387638SSascha Wildner { NULL, NULL }, /* Oc */
22480387638SSascha Wildner { termp_bk_pre, termp_bk_post }, /* Bk */
22580387638SSascha Wildner { NULL, NULL }, /* Ek */
22654ba9607SSascha Wildner { NULL, NULL }, /* Bt */
22780387638SSascha Wildner { NULL, NULL }, /* Hf */
228070c62a6SFranco Fichtner { termp_under_pre, NULL }, /* Fr */
22954ba9607SSascha Wildner { NULL, NULL }, /* Ud */
23080387638SSascha Wildner { NULL, termp_lb_post }, /* Lb */
23154ba9607SSascha Wildner { termp_abort_pre, NULL }, /* Lp */
23280387638SSascha Wildner { termp_lk_pre, NULL }, /* Lk */
23380387638SSascha Wildner { termp_under_pre, NULL }, /* Mt */
23480387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Brq */
23580387638SSascha Wildner { termp_quote_pre, termp_quote_post }, /* Bro */
23680387638SSascha Wildner { NULL, NULL }, /* Brc */
23780387638SSascha Wildner { NULL, termp____post }, /* %C */
23854ba9607SSascha Wildner { termp_skip_pre, NULL }, /* Es */
239070c62a6SFranco Fichtner { termp_quote_pre, termp_quote_post }, /* En */
24054ba9607SSascha Wildner { termp_xx_pre, termp_xx_post }, /* Dx */
24180387638SSascha Wildner { NULL, termp____post }, /* %Q */
242f88b6c16SFranco Fichtner { NULL, termp____post }, /* %U */
24380387638SSascha Wildner { NULL, NULL }, /* Ta */
244*99db7d0eSSascha Wildner { termp_skip_pre, NULL }, /* Tg */
24580387638SSascha Wildner };
24680387638SSascha Wildner
24780387638SSascha Wildner
24880387638SSascha Wildner void
terminal_mdoc(void * arg,const struct roff_meta * mdoc)24954ba9607SSascha Wildner terminal_mdoc(void *arg, const struct roff_meta *mdoc)
25080387638SSascha Wildner {
251*99db7d0eSSascha Wildner struct roff_node *n, *nn;
25280387638SSascha Wildner struct termp *p;
25354ba9607SSascha Wildner size_t save_defindent;
25480387638SSascha Wildner
25580387638SSascha Wildner p = (struct termp *)arg;
25654ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin = p->defrmargin;
25754ba9607SSascha Wildner term_tab_set(p, NULL);
25854ba9607SSascha Wildner term_tab_set(p, "T");
25954ba9607SSascha Wildner term_tab_set(p, ".5i");
26080387638SSascha Wildner
26154ba9607SSascha Wildner n = mdoc->first->child;
26254ba9607SSascha Wildner if (p->synopsisonly) {
263*99db7d0eSSascha Wildner for (nn = NULL; n != NULL; n = n->next) {
264*99db7d0eSSascha Wildner if (n->tok != MDOC_Sh)
265*99db7d0eSSascha Wildner continue;
266*99db7d0eSSascha Wildner if (n->sec == SEC_SYNOPSIS)
26754ba9607SSascha Wildner break;
268*99db7d0eSSascha Wildner if (nn == NULL && n->sec == SEC_NAME)
269*99db7d0eSSascha Wildner nn = n;
270070c62a6SFranco Fichtner }
271*99db7d0eSSascha Wildner if (n == NULL)
272*99db7d0eSSascha Wildner n = nn;
273*99db7d0eSSascha Wildner p->flags |= TERMP_NOSPACE;
274*99db7d0eSSascha Wildner if (n != NULL && (n = n->child->next->child) != NULL)
275*99db7d0eSSascha Wildner print_mdoc_nodelist(p, NULL, mdoc, n);
276*99db7d0eSSascha Wildner term_newln(p);
27754ba9607SSascha Wildner } else {
27854ba9607SSascha Wildner save_defindent = p->defindent;
27954ba9607SSascha Wildner if (p->defindent == 0)
28054ba9607SSascha Wildner p->defindent = 5;
28154ba9607SSascha Wildner term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
28254ba9607SSascha Wildner while (n != NULL &&
28354ba9607SSascha Wildner (n->type == ROFFT_COMMENT ||
28454ba9607SSascha Wildner n->flags & NODE_NOPRT))
28554ba9607SSascha Wildner n = n->next;
28654ba9607SSascha Wildner if (n != NULL) {
28754ba9607SSascha Wildner if (n->tok != MDOC_Sh)
28854ba9607SSascha Wildner term_vspace(p);
28954ba9607SSascha Wildner print_mdoc_nodelist(p, NULL, mdoc, n);
29054ba9607SSascha Wildner }
29180387638SSascha Wildner term_end(p);
29254ba9607SSascha Wildner p->defindent = save_defindent;
29354ba9607SSascha Wildner }
29480387638SSascha Wildner }
29580387638SSascha Wildner
29680387638SSascha Wildner static void
print_mdoc_nodelist(DECL_ARGS)29780387638SSascha Wildner print_mdoc_nodelist(DECL_ARGS)
29880387638SSascha Wildner {
29954ba9607SSascha Wildner while (n != NULL) {
300f88b6c16SFranco Fichtner print_mdoc_node(p, pair, meta, n);
30154ba9607SSascha Wildner n = n->next;
30254ba9607SSascha Wildner }
30380387638SSascha Wildner }
30480387638SSascha Wildner
30580387638SSascha Wildner static void
print_mdoc_node(DECL_ARGS)30680387638SSascha Wildner print_mdoc_node(DECL_ARGS)
30780387638SSascha Wildner {
30854ba9607SSascha Wildner const struct mdoc_term_act *act;
30980387638SSascha Wildner struct termpair npair;
31080387638SSascha Wildner size_t offset, rmargin;
31154ba9607SSascha Wildner int chld;
31254ba9607SSascha Wildner
31354ba9607SSascha Wildner /*
31454ba9607SSascha Wildner * In no-fill mode, break the output line at the beginning
31554ba9607SSascha Wildner * of new input lines except after \c, and nowhere else.
31654ba9607SSascha Wildner */
31754ba9607SSascha Wildner
31854ba9607SSascha Wildner if (n->flags & NODE_NOFILL) {
31954ba9607SSascha Wildner if (n->flags & NODE_LINE &&
32054ba9607SSascha Wildner (p->flags & TERMP_NONEWLINE) == 0)
32154ba9607SSascha Wildner term_newln(p);
32254ba9607SSascha Wildner p->flags |= TERMP_BRNEVER;
32354ba9607SSascha Wildner } else
32454ba9607SSascha Wildner p->flags &= ~TERMP_BRNEVER;
32554ba9607SSascha Wildner
32654ba9607SSascha Wildner if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
32754ba9607SSascha Wildner return;
32880387638SSascha Wildner
32980387638SSascha Wildner chld = 1;
33054ba9607SSascha Wildner offset = p->tcol->offset;
33154ba9607SSascha Wildner rmargin = p->tcol->rmargin;
33254ba9607SSascha Wildner n->flags &= ~NODE_ENDED;
33354ba9607SSascha Wildner n->prev_font = p->fonti;
33480387638SSascha Wildner
33580387638SSascha Wildner memset(&npair, 0, sizeof(struct termpair));
33680387638SSascha Wildner npair.ppair = pair;
33780387638SSascha Wildner
338*99db7d0eSSascha Wildner if (n->flags & NODE_ID && n->tok != MDOC_Pp &&
339*99db7d0eSSascha Wildner (n->tok != MDOC_It || n->type != ROFFT_BLOCK))
340*99db7d0eSSascha Wildner term_tag_write(n, p->line);
341*99db7d0eSSascha Wildner
34280387638SSascha Wildner /*
34380387638SSascha Wildner * Keeps only work until the end of a line. If a keep was
34480387638SSascha Wildner * invoked in a prior line, revert it to PREKEEP.
34580387638SSascha Wildner */
34680387638SSascha Wildner
34754ba9607SSascha Wildner if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
34880387638SSascha Wildner p->flags &= ~TERMP_KEEP;
34980387638SSascha Wildner p->flags |= TERMP_PREKEEP;
35080387638SSascha Wildner }
35180387638SSascha Wildner
35280387638SSascha Wildner /*
35360e1e752SSascha Wildner * After the keep flags have been set up, we may now
35460e1e752SSascha Wildner * produce output. Note that some pre-handlers do so.
35560e1e752SSascha Wildner */
35660e1e752SSascha Wildner
357*99db7d0eSSascha Wildner act = NULL;
35860e1e752SSascha Wildner switch (n->type) {
35954ba9607SSascha Wildner case ROFFT_TEXT:
36054ba9607SSascha Wildner if (n->flags & NODE_LINE) {
36154ba9607SSascha Wildner switch (*n->string) {
36254ba9607SSascha Wildner case '\0':
36354ba9607SSascha Wildner if (p->flags & TERMP_NONEWLINE)
36460e1e752SSascha Wildner term_newln(p);
36554ba9607SSascha Wildner else
36654ba9607SSascha Wildner term_vspace(p);
36754ba9607SSascha Wildner return;
36854ba9607SSascha Wildner case ' ':
36954ba9607SSascha Wildner if ((p->flags & TERMP_NONEWLINE) == 0)
37054ba9607SSascha Wildner term_newln(p);
37154ba9607SSascha Wildner break;
37254ba9607SSascha Wildner default:
37354ba9607SSascha Wildner break;
37454ba9607SSascha Wildner }
37554ba9607SSascha Wildner }
37654ba9607SSascha Wildner if (NODE_DELIMC & n->flags)
37760e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
37860e1e752SSascha Wildner term_word(p, n->string);
37954ba9607SSascha Wildner if (NODE_DELIMO & n->flags)
38060e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
38160e1e752SSascha Wildner break;
38254ba9607SSascha Wildner case ROFFT_EQN:
38354ba9607SSascha Wildner if ( ! (n->flags & NODE_LINE))
38454ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
38536342e81SSascha Wildner term_eqn(p, n->eqn);
38654ba9607SSascha Wildner if (n->next != NULL && ! (n->next->flags & NODE_LINE))
38754ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
38860e1e752SSascha Wildner break;
38954ba9607SSascha Wildner case ROFFT_TBL:
39054ba9607SSascha Wildner if (p->tbl.cols == NULL)
39154ba9607SSascha Wildner term_newln(p);
39260e1e752SSascha Wildner term_tbl(p, n->span);
39360e1e752SSascha Wildner break;
39460e1e752SSascha Wildner default:
39554ba9607SSascha Wildner if (n->tok < ROFF_MAX) {
39654ba9607SSascha Wildner roff_term_pre(p, n);
39754ba9607SSascha Wildner return;
39854ba9607SSascha Wildner }
39954ba9607SSascha Wildner assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
40054ba9607SSascha Wildner act = mdoc_term_acts + (n->tok - MDOC_Dd);
40154ba9607SSascha Wildner if (act->pre != NULL &&
40254ba9607SSascha Wildner (n->end == ENDBODY_NOT || n->child != NULL))
40354ba9607SSascha Wildner chld = (*act->pre)(p, &npair, meta, n);
40460e1e752SSascha Wildner break;
40560e1e752SSascha Wildner }
40660e1e752SSascha Wildner
40780387638SSascha Wildner if (chld && n->child)
408f88b6c16SFranco Fichtner print_mdoc_nodelist(p, &npair, meta, n->child);
40980387638SSascha Wildner
410f88b6c16SFranco Fichtner term_fontpopq(p,
41154ba9607SSascha Wildner (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
41280387638SSascha Wildner
41380387638SSascha Wildner switch (n->type) {
41454ba9607SSascha Wildner case ROFFT_TEXT:
41580387638SSascha Wildner break;
41654ba9607SSascha Wildner case ROFFT_TBL:
41780387638SSascha Wildner break;
41854ba9607SSascha Wildner case ROFFT_EQN:
41960e1e752SSascha Wildner break;
42080387638SSascha Wildner default:
42154ba9607SSascha Wildner if (act->post == NULL || n->flags & NODE_ENDED)
42280387638SSascha Wildner break;
42354ba9607SSascha Wildner (void)(*act->post)(p, &npair, meta, n);
42480387638SSascha Wildner
42580387638SSascha Wildner /*
42680387638SSascha Wildner * Explicit end tokens not only call the post
42780387638SSascha Wildner * handler, but also tell the respective block
42880387638SSascha Wildner * that it must not call the post handler again.
42980387638SSascha Wildner */
43080387638SSascha Wildner if (ENDBODY_NOT != n->end)
43154ba9607SSascha Wildner n->body->flags |= NODE_ENDED;
43280387638SSascha Wildner break;
43380387638SSascha Wildner }
43480387638SSascha Wildner
43554ba9607SSascha Wildner if (NODE_EOS & n->flags)
43680387638SSascha Wildner p->flags |= TERMP_SENTENCE;
43780387638SSascha Wildner
43854ba9607SSascha Wildner if (n->type != ROFFT_TEXT)
43954ba9607SSascha Wildner p->tcol->offset = offset;
44054ba9607SSascha Wildner p->tcol->rmargin = rmargin;
441070c62a6SFranco Fichtner }
44280387638SSascha Wildner
44380387638SSascha Wildner static void
print_mdoc_foot(struct termp * p,const struct roff_meta * meta)44454ba9607SSascha Wildner print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
44580387638SSascha Wildner {
44654ba9607SSascha Wildner size_t sz;
44780387638SSascha Wildner
44880387638SSascha Wildner term_fontrepl(p, TERMFONT_NONE);
44980387638SSascha Wildner
45080387638SSascha Wildner /*
45180387638SSascha Wildner * Output the footer in new-groff style, that is, three columns
45280387638SSascha Wildner * with the middle being the manual date and flanking columns
45380387638SSascha Wildner * being the operating system:
45480387638SSascha Wildner *
45580387638SSascha Wildner * SYSTEM DATE SYSTEM
45680387638SSascha Wildner */
45780387638SSascha Wildner
45880387638SSascha Wildner term_vspace(p);
45980387638SSascha Wildner
46054ba9607SSascha Wildner p->tcol->offset = 0;
46154ba9607SSascha Wildner sz = term_strlen(p, meta->date);
46254ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin > sz ?
46354ba9607SSascha Wildner (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
4647888c61dSFranco Fichtner p->trailspace = 1;
46580387638SSascha Wildner p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
46680387638SSascha Wildner
467f88b6c16SFranco Fichtner term_word(p, meta->os);
46880387638SSascha Wildner term_flushln(p);
46980387638SSascha Wildner
47054ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
47154ba9607SSascha Wildner sz = term_strlen(p, meta->os);
47254ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
47336342e81SSascha Wildner p->flags |= TERMP_NOSPACE;
47480387638SSascha Wildner
475f88b6c16SFranco Fichtner term_word(p, meta->date);
47680387638SSascha Wildner term_flushln(p);
47780387638SSascha Wildner
47854ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
47954ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin;
4807888c61dSFranco Fichtner p->trailspace = 0;
48180387638SSascha Wildner p->flags &= ~TERMP_NOBREAK;
48236342e81SSascha Wildner p->flags |= TERMP_NOSPACE;
48380387638SSascha Wildner
484f88b6c16SFranco Fichtner term_word(p, meta->os);
48580387638SSascha Wildner term_flushln(p);
48680387638SSascha Wildner
48754ba9607SSascha Wildner p->tcol->offset = 0;
48854ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin;
48980387638SSascha Wildner p->flags = 0;
49080387638SSascha Wildner }
49180387638SSascha Wildner
49280387638SSascha Wildner static void
print_mdoc_head(struct termp * p,const struct roff_meta * meta)49354ba9607SSascha Wildner print_mdoc_head(struct termp *p, const struct roff_meta *meta)
49480387638SSascha Wildner {
495070c62a6SFranco Fichtner char *volume, *title;
496070c62a6SFranco Fichtner size_t vollen, titlen;
49780387638SSascha Wildner
49880387638SSascha Wildner /*
49980387638SSascha Wildner * The header is strange. It has three components, which are
50080387638SSascha Wildner * really two with the first duplicated. It goes like this:
50180387638SSascha Wildner *
50280387638SSascha Wildner * IDENTIFIER TITLE IDENTIFIER
50380387638SSascha Wildner *
50480387638SSascha Wildner * The IDENTIFIER is NAME(SECTION), which is the command-name
50580387638SSascha Wildner * (if given, or "unknown" if not) followed by the manual page
50680387638SSascha Wildner * section. These are given in `Dt'. The TITLE is a free-form
50780387638SSascha Wildner * string depending on the manual volume. If not specified, it
50880387638SSascha Wildner * switches on the manual section.
50980387638SSascha Wildner */
51080387638SSascha Wildner
511f88b6c16SFranco Fichtner assert(meta->vol);
512070c62a6SFranco Fichtner if (NULL == meta->arch)
513070c62a6SFranco Fichtner volume = mandoc_strdup(meta->vol);
514070c62a6SFranco Fichtner else
515070c62a6SFranco Fichtner mandoc_asprintf(&volume, "%s (%s)",
516070c62a6SFranco Fichtner meta->vol, meta->arch);
517070c62a6SFranco Fichtner vollen = term_strlen(p, volume);
51880387638SSascha Wildner
519070c62a6SFranco Fichtner if (NULL == meta->msec)
520070c62a6SFranco Fichtner title = mandoc_strdup(meta->title);
521070c62a6SFranco Fichtner else
522070c62a6SFranco Fichtner mandoc_asprintf(&title, "%s(%s)",
523070c62a6SFranco Fichtner meta->title, meta->msec);
52436342e81SSascha Wildner titlen = term_strlen(p, title);
52580387638SSascha Wildner
52680387638SSascha Wildner p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
5277888c61dSFranco Fichtner p->trailspace = 1;
52854ba9607SSascha Wildner p->tcol->offset = 0;
52954ba9607SSascha Wildner p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
530070c62a6SFranco Fichtner (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
53154ba9607SSascha Wildner vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
53280387638SSascha Wildner
53380387638SSascha Wildner term_word(p, title);
53480387638SSascha Wildner term_flushln(p);
53580387638SSascha Wildner
53636342e81SSascha Wildner p->flags |= TERMP_NOSPACE;
53754ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
53854ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + vollen + titlen <
53954ba9607SSascha Wildner p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
54080387638SSascha Wildner
541070c62a6SFranco Fichtner term_word(p, volume);
54280387638SSascha Wildner term_flushln(p);
54380387638SSascha Wildner
54436342e81SSascha Wildner p->flags &= ~TERMP_NOBREAK;
5457888c61dSFranco Fichtner p->trailspace = 0;
54654ba9607SSascha Wildner if (p->tcol->rmargin + titlen <= p->maxrmargin) {
54736342e81SSascha Wildner p->flags |= TERMP_NOSPACE;
54854ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
54954ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin;
55080387638SSascha Wildner term_word(p, title);
55180387638SSascha Wildner term_flushln(p);
55236342e81SSascha Wildner }
55380387638SSascha Wildner
55436342e81SSascha Wildner p->flags &= ~TERMP_NOSPACE;
55554ba9607SSascha Wildner p->tcol->offset = 0;
55654ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin;
557070c62a6SFranco Fichtner free(title);
558070c62a6SFranco Fichtner free(volume);
55980387638SSascha Wildner }
56080387638SSascha Wildner
56154ba9607SSascha Wildner static int
a2width(const struct termp * p,const char * v)56280387638SSascha Wildner a2width(const struct termp *p, const char *v)
56380387638SSascha Wildner {
56480387638SSascha Wildner struct roffsu su;
56554ba9607SSascha Wildner const char *end;
56680387638SSascha Wildner
56754ba9607SSascha Wildner end = a2roffsu(v, &su, SCALE_MAX);
56854ba9607SSascha Wildner if (end == NULL || *end != '\0') {
56980387638SSascha Wildner SCALE_HS_INIT(&su, term_strlen(p, v));
57054ba9607SSascha Wildner su.scale /= term_strlen(p, "0");
57180387638SSascha Wildner }
57254ba9607SSascha Wildner return term_hen(p, &su);
57380387638SSascha Wildner }
57480387638SSascha Wildner
57580387638SSascha Wildner /*
57680387638SSascha Wildner * Determine how much space to print out before block elements of `It'
57780387638SSascha Wildner * (and thus `Bl') and `Bd'. And then go ahead and print that space,
57880387638SSascha Wildner * too.
57980387638SSascha Wildner */
58080387638SSascha Wildner static void
print_bvspace(struct termp * p,struct roff_node * bl,struct roff_node * n)581*99db7d0eSSascha Wildner print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n)
58280387638SSascha Wildner {
583*99db7d0eSSascha Wildner struct roff_node *nn;
58436342e81SSascha Wildner
58580387638SSascha Wildner term_newln(p);
58680387638SSascha Wildner
587*99db7d0eSSascha Wildner if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) ||
588*99db7d0eSSascha Wildner (bl->tok == MDOC_Bl && bl->norm->Bl.comp))
58980387638SSascha Wildner return;
59080387638SSascha Wildner
59180387638SSascha Wildner /* Do not vspace directly after Ss/Sh. */
59280387638SSascha Wildner
59354ba9607SSascha Wildner nn = n;
594*99db7d0eSSascha Wildner while (roff_node_prev(nn) == NULL) {
59554ba9607SSascha Wildner do {
59654ba9607SSascha Wildner nn = nn->parent;
59754ba9607SSascha Wildner if (nn->type == ROFFT_ROOT)
59880387638SSascha Wildner return;
59954ba9607SSascha Wildner } while (nn->type != ROFFT_BLOCK);
60054ba9607SSascha Wildner if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
60180387638SSascha Wildner return;
60254ba9607SSascha Wildner if (nn->tok == MDOC_It &&
60354ba9607SSascha Wildner nn->parent->parent->norm->Bl.type != LIST_item)
60480387638SSascha Wildner break;
60580387638SSascha Wildner }
60680387638SSascha Wildner
607*99db7d0eSSascha Wildner /*
608*99db7d0eSSascha Wildner * No vertical space after:
609*99db7d0eSSascha Wildner * items in .Bl -column
610*99db7d0eSSascha Wildner * items without a body in .Bl -diag
611*99db7d0eSSascha Wildner */
61280387638SSascha Wildner
613*99db7d0eSSascha Wildner if (bl->tok != MDOC_Bl ||
614*99db7d0eSSascha Wildner n->prev == NULL || n->prev->tok != MDOC_It ||
615*99db7d0eSSascha Wildner (bl->norm->Bl.type != LIST_column &&
616*99db7d0eSSascha Wildner (bl->norm->Bl.type != LIST_diag ||
617*99db7d0eSSascha Wildner n->prev->body->child != NULL)))
61880387638SSascha Wildner term_vspace(p);
61980387638SSascha Wildner }
62080387638SSascha Wildner
62180387638SSascha Wildner
622070c62a6SFranco Fichtner static int
termp_it_pre(DECL_ARGS)62380387638SSascha Wildner termp_it_pre(DECL_ARGS)
62480387638SSascha Wildner {
62554ba9607SSascha Wildner struct roffsu su;
626070c62a6SFranco Fichtner char buf[24];
62754ba9607SSascha Wildner const struct roff_node *bl, *nn;
62854ba9607SSascha Wildner size_t ncols, dcol;
62954ba9607SSascha Wildner int i, offset, width;
63080387638SSascha Wildner enum mdoc_list type;
63180387638SSascha Wildner
63254ba9607SSascha Wildner if (n->type == ROFFT_BLOCK) {
63380387638SSascha Wildner print_bvspace(p, n->parent->parent, n);
634*99db7d0eSSascha Wildner if (n->flags & NODE_ID)
635*99db7d0eSSascha Wildner term_tag_write(n, p->line);
63654ba9607SSascha Wildner return 1;
63780387638SSascha Wildner }
63880387638SSascha Wildner
63980387638SSascha Wildner bl = n->parent->parent->parent;
64080387638SSascha Wildner type = bl->norm->Bl.type;
64180387638SSascha Wildner
64280387638SSascha Wildner /*
64354ba9607SSascha Wildner * Defaults for specific list types.
64454ba9607SSascha Wildner */
64554ba9607SSascha Wildner
64654ba9607SSascha Wildner switch (type) {
64754ba9607SSascha Wildner case LIST_bullet:
64854ba9607SSascha Wildner case LIST_dash:
64954ba9607SSascha Wildner case LIST_hyphen:
65054ba9607SSascha Wildner case LIST_enum:
65154ba9607SSascha Wildner width = term_len(p, 2);
65254ba9607SSascha Wildner break;
65354ba9607SSascha Wildner case LIST_hang:
65454ba9607SSascha Wildner case LIST_tag:
65554ba9607SSascha Wildner width = term_len(p, 8);
65654ba9607SSascha Wildner break;
65754ba9607SSascha Wildner case LIST_column:
65854ba9607SSascha Wildner width = term_len(p, 10);
65954ba9607SSascha Wildner break;
66054ba9607SSascha Wildner default:
66154ba9607SSascha Wildner width = 0;
66254ba9607SSascha Wildner break;
66354ba9607SSascha Wildner }
66454ba9607SSascha Wildner offset = 0;
66554ba9607SSascha Wildner
66654ba9607SSascha Wildner /*
66780387638SSascha Wildner * First calculate width and offset. This is pretty easy unless
66880387638SSascha Wildner * we're a -column list, in which case all prior columns must
66980387638SSascha Wildner * be accounted for.
67080387638SSascha Wildner */
67180387638SSascha Wildner
67254ba9607SSascha Wildner if (bl->norm->Bl.offs != NULL) {
67354ba9607SSascha Wildner offset = a2width(p, bl->norm->Bl.offs);
67454ba9607SSascha Wildner if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
67554ba9607SSascha Wildner offset = -p->tcol->offset;
67654ba9607SSascha Wildner else if (offset > SHRT_MAX)
67754ba9607SSascha Wildner offset = 0;
67854ba9607SSascha Wildner }
67980387638SSascha Wildner
68080387638SSascha Wildner switch (type) {
681070c62a6SFranco Fichtner case LIST_column:
68254ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
68380387638SSascha Wildner break;
68480387638SSascha Wildner
68580387638SSascha Wildner /*
68680387638SSascha Wildner * Imitate groff's column handling:
68780387638SSascha Wildner * - For each earlier column, add its width.
68880387638SSascha Wildner * - For less than 5 columns, add four more blanks per
68980387638SSascha Wildner * column.
69080387638SSascha Wildner * - For exactly 5 columns, add three more blank per
69180387638SSascha Wildner * column.
69280387638SSascha Wildner * - For more than 5 columns, add only one column.
69380387638SSascha Wildner */
69480387638SSascha Wildner ncols = bl->norm->Bl.ncols;
69580387638SSascha Wildner dcol = ncols < 5 ? term_len(p, 4) :
69680387638SSascha Wildner ncols == 5 ? term_len(p, 3) : term_len(p, 1);
69780387638SSascha Wildner
69880387638SSascha Wildner /*
69954ba9607SSascha Wildner * Calculate the offset by applying all prior ROFFT_BODY,
70054ba9607SSascha Wildner * so we stop at the ROFFT_HEAD (nn->prev == NULL).
70180387638SSascha Wildner */
70280387638SSascha Wildner
70380387638SSascha Wildner for (i = 0, nn = n->prev;
70480387638SSascha Wildner nn->prev && i < (int)ncols;
70554ba9607SSascha Wildner nn = nn->prev, i++) {
70654ba9607SSascha Wildner SCALE_HS_INIT(&su,
70754ba9607SSascha Wildner term_strlen(p, bl->norm->Bl.cols[i]));
70854ba9607SSascha Wildner su.scale /= term_strlen(p, "0");
70954ba9607SSascha Wildner offset += term_hen(p, &su) + dcol;
71054ba9607SSascha Wildner }
71180387638SSascha Wildner
71280387638SSascha Wildner /*
71380387638SSascha Wildner * When exceeding the declared number of columns, leave
71480387638SSascha Wildner * the remaining widths at 0. This will later be
71580387638SSascha Wildner * adjusted to the default width of 10, or, for the last
71680387638SSascha Wildner * column, stretched to the right margin.
71780387638SSascha Wildner */
71880387638SSascha Wildner if (i >= (int)ncols)
71980387638SSascha Wildner break;
72080387638SSascha Wildner
72180387638SSascha Wildner /*
72280387638SSascha Wildner * Use the declared column widths, extended as explained
72380387638SSascha Wildner * in the preceding paragraph.
72480387638SSascha Wildner */
72554ba9607SSascha Wildner SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
72654ba9607SSascha Wildner su.scale /= term_strlen(p, "0");
72754ba9607SSascha Wildner width = term_hen(p, &su) + dcol;
72880387638SSascha Wildner break;
72980387638SSascha Wildner default:
73080387638SSascha Wildner if (NULL == bl->norm->Bl.width)
73180387638SSascha Wildner break;
73280387638SSascha Wildner
73380387638SSascha Wildner /*
73480387638SSascha Wildner * Note: buffer the width by 2, which is groff's magic
73580387638SSascha Wildner * number for buffering single arguments. See the above
73680387638SSascha Wildner * handling for column for how this changes.
73780387638SSascha Wildner */
73880387638SSascha Wildner width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
73954ba9607SSascha Wildner if (width < 0 && (size_t)(-width) > p->tcol->offset)
74054ba9607SSascha Wildner width = -p->tcol->offset;
74154ba9607SSascha Wildner else if (width > SHRT_MAX)
74254ba9607SSascha Wildner width = 0;
74380387638SSascha Wildner break;
74480387638SSascha Wildner }
74580387638SSascha Wildner
74680387638SSascha Wildner /*
74780387638SSascha Wildner * Whitespace control. Inset bodies need an initial space,
74880387638SSascha Wildner * while diagonal bodies need two.
74980387638SSascha Wildner */
75080387638SSascha Wildner
75180387638SSascha Wildner p->flags |= TERMP_NOSPACE;
75280387638SSascha Wildner
75380387638SSascha Wildner switch (type) {
754070c62a6SFranco Fichtner case LIST_diag:
75554ba9607SSascha Wildner if (n->type == ROFFT_BODY)
75680387638SSascha Wildner term_word(p, "\\ \\ ");
75780387638SSascha Wildner break;
758070c62a6SFranco Fichtner case LIST_inset:
75954ba9607SSascha Wildner if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
76080387638SSascha Wildner term_word(p, "\\ ");
76180387638SSascha Wildner break;
76280387638SSascha Wildner default:
76380387638SSascha Wildner break;
76480387638SSascha Wildner }
76580387638SSascha Wildner
76680387638SSascha Wildner p->flags |= TERMP_NOSPACE;
76780387638SSascha Wildner
76880387638SSascha Wildner switch (type) {
769070c62a6SFranco Fichtner case LIST_diag:
77054ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
77180387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
77280387638SSascha Wildner break;
77380387638SSascha Wildner default:
77480387638SSascha Wildner break;
77580387638SSascha Wildner }
77680387638SSascha Wildner
77780387638SSascha Wildner /*
77880387638SSascha Wildner * Pad and break control. This is the tricky part. These flags
77980387638SSascha Wildner * are documented in term_flushln() in term.c. Note that we're
78080387638SSascha Wildner * going to unset all of these flags in termp_it_post() when we
78180387638SSascha Wildner * exit.
78280387638SSascha Wildner */
78380387638SSascha Wildner
78480387638SSascha Wildner switch (type) {
785070c62a6SFranco Fichtner case LIST_enum:
786070c62a6SFranco Fichtner case LIST_bullet:
787070c62a6SFranco Fichtner case LIST_dash:
788070c62a6SFranco Fichtner case LIST_hyphen:
78954ba9607SSascha Wildner if (n->type == ROFFT_HEAD) {
79054ba9607SSascha Wildner p->flags |= TERMP_NOBREAK | TERMP_HANG;
7917888c61dSFranco Fichtner p->trailspace = 1;
79254ba9607SSascha Wildner } else if (width <= (int)term_len(p, 2))
79354ba9607SSascha Wildner p->flags |= TERMP_NOPAD;
79480387638SSascha Wildner break;
795070c62a6SFranco Fichtner case LIST_hang:
79654ba9607SSascha Wildner if (n->type != ROFFT_HEAD)
79780387638SSascha Wildner break;
798070c62a6SFranco Fichtner p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
7997888c61dSFranco Fichtner p->trailspace = 1;
80080387638SSascha Wildner break;
801070c62a6SFranco Fichtner case LIST_tag:
80254ba9607SSascha Wildner if (n->type != ROFFT_HEAD)
80380387638SSascha Wildner break;
8047888c61dSFranco Fichtner
80554ba9607SSascha Wildner p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
8067888c61dSFranco Fichtner p->trailspace = 2;
8077888c61dSFranco Fichtner
80880387638SSascha Wildner if (NULL == n->next || NULL == n->next->child)
80954ba9607SSascha Wildner p->flags |= TERMP_HANG;
81080387638SSascha Wildner break;
811070c62a6SFranco Fichtner case LIST_column:
81254ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
81380387638SSascha Wildner break;
81480387638SSascha Wildner
8157888c61dSFranco Fichtner if (NULL == n->next) {
81680387638SSascha Wildner p->flags &= ~TERMP_NOBREAK;
8177888c61dSFranco Fichtner p->trailspace = 0;
8187888c61dSFranco Fichtner } else {
81980387638SSascha Wildner p->flags |= TERMP_NOBREAK;
8207888c61dSFranco Fichtner p->trailspace = 1;
8217888c61dSFranco Fichtner }
82280387638SSascha Wildner
82380387638SSascha Wildner break;
824070c62a6SFranco Fichtner case LIST_diag:
82554ba9607SSascha Wildner if (n->type != ROFFT_HEAD)
8267888c61dSFranco Fichtner break;
827070c62a6SFranco Fichtner p->flags |= TERMP_NOBREAK | TERMP_BRIND;
8287888c61dSFranco Fichtner p->trailspace = 1;
82980387638SSascha Wildner break;
83080387638SSascha Wildner default:
83180387638SSascha Wildner break;
83280387638SSascha Wildner }
83380387638SSascha Wildner
83480387638SSascha Wildner /*
83580387638SSascha Wildner * Margin control. Set-head-width lists have their right
83680387638SSascha Wildner * margins shortened. The body for these lists has the offset
83780387638SSascha Wildner * necessarily lengthened. Everybody gets the offset.
83880387638SSascha Wildner */
83980387638SSascha Wildner
84054ba9607SSascha Wildner p->tcol->offset += offset;
84180387638SSascha Wildner
84280387638SSascha Wildner switch (type) {
843070c62a6SFranco Fichtner case LIST_bullet:
844070c62a6SFranco Fichtner case LIST_dash:
845070c62a6SFranco Fichtner case LIST_enum:
846070c62a6SFranco Fichtner case LIST_hyphen:
84754ba9607SSascha Wildner case LIST_hang:
848070c62a6SFranco Fichtner case LIST_tag:
84954ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
85054ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + width;
85154ba9607SSascha Wildner else
85254ba9607SSascha Wildner p->tcol->offset += width;
85380387638SSascha Wildner break;
854070c62a6SFranco Fichtner case LIST_column:
85580387638SSascha Wildner assert(width);
85654ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + width;
85780387638SSascha Wildner /*
85880387638SSascha Wildner * XXX - this behaviour is not documented: the
85980387638SSascha Wildner * right-most column is filled to the right margin.
86080387638SSascha Wildner */
86154ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
86280387638SSascha Wildner break;
86354ba9607SSascha Wildner if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
86454ba9607SSascha Wildner p->tcol->rmargin = p->maxrmargin;
86580387638SSascha Wildner break;
86680387638SSascha Wildner default:
86780387638SSascha Wildner break;
86880387638SSascha Wildner }
86980387638SSascha Wildner
87080387638SSascha Wildner /*
87180387638SSascha Wildner * The dash, hyphen, bullet and enum lists all have a special
87280387638SSascha Wildner * HEAD character (temporarily bold, in some cases).
87380387638SSascha Wildner */
87480387638SSascha Wildner
87554ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
87680387638SSascha Wildner switch (type) {
877070c62a6SFranco Fichtner case LIST_bullet:
87880387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
87980387638SSascha Wildner term_word(p, "\\[bu]");
88080387638SSascha Wildner term_fontpop(p);
88180387638SSascha Wildner break;
882070c62a6SFranco Fichtner case LIST_dash:
883070c62a6SFranco Fichtner case LIST_hyphen:
88480387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
88554ba9607SSascha Wildner term_word(p, "-");
88680387638SSascha Wildner term_fontpop(p);
88780387638SSascha Wildner break;
888070c62a6SFranco Fichtner case LIST_enum:
88980387638SSascha Wildner (pair->ppair->ppair->count)++;
890070c62a6SFranco Fichtner (void)snprintf(buf, sizeof(buf), "%d.",
89180387638SSascha Wildner pair->ppair->ppair->count);
89280387638SSascha Wildner term_word(p, buf);
89380387638SSascha Wildner break;
89480387638SSascha Wildner default:
89580387638SSascha Wildner break;
89680387638SSascha Wildner }
89780387638SSascha Wildner
89880387638SSascha Wildner /*
89980387638SSascha Wildner * If we're not going to process our children, indicate so here.
90080387638SSascha Wildner */
90180387638SSascha Wildner
90280387638SSascha Wildner switch (type) {
903070c62a6SFranco Fichtner case LIST_bullet:
904070c62a6SFranco Fichtner case LIST_item:
905070c62a6SFranco Fichtner case LIST_dash:
906070c62a6SFranco Fichtner case LIST_hyphen:
907070c62a6SFranco Fichtner case LIST_enum:
90854ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
90954ba9607SSascha Wildner return 0;
91080387638SSascha Wildner break;
911070c62a6SFranco Fichtner case LIST_column:
91254ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
91354ba9607SSascha Wildner return 0;
91454ba9607SSascha Wildner p->minbl = 0;
91580387638SSascha Wildner break;
91680387638SSascha Wildner default:
91780387638SSascha Wildner break;
91880387638SSascha Wildner }
91980387638SSascha Wildner
92054ba9607SSascha Wildner return 1;
92180387638SSascha Wildner }
92280387638SSascha Wildner
92380387638SSascha Wildner static void
termp_it_post(DECL_ARGS)92480387638SSascha Wildner termp_it_post(DECL_ARGS)
92580387638SSascha Wildner {
92680387638SSascha Wildner enum mdoc_list type;
92780387638SSascha Wildner
92854ba9607SSascha Wildner if (n->type == ROFFT_BLOCK)
92980387638SSascha Wildner return;
93080387638SSascha Wildner
93180387638SSascha Wildner type = n->parent->parent->parent->norm->Bl.type;
93280387638SSascha Wildner
93380387638SSascha Wildner switch (type) {
934070c62a6SFranco Fichtner case LIST_item:
935070c62a6SFranco Fichtner case LIST_diag:
936070c62a6SFranco Fichtner case LIST_inset:
93754ba9607SSascha Wildner if (n->type == ROFFT_BODY)
93880387638SSascha Wildner term_newln(p);
93980387638SSascha Wildner break;
940070c62a6SFranco Fichtner case LIST_column:
94154ba9607SSascha Wildner if (n->type == ROFFT_BODY)
94280387638SSascha Wildner term_flushln(p);
94380387638SSascha Wildner break;
94480387638SSascha Wildner default:
94580387638SSascha Wildner term_newln(p);
94680387638SSascha Wildner break;
94780387638SSascha Wildner }
94880387638SSascha Wildner
94980387638SSascha Wildner /*
95080387638SSascha Wildner * Now that our output is flushed, we can reset our tags. Since
95180387638SSascha Wildner * only `It' sets these flags, we're free to assume that nobody
95280387638SSascha Wildner * has munged them in the meanwhile.
95380387638SSascha Wildner */
95480387638SSascha Wildner
95554ba9607SSascha Wildner p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
9567888c61dSFranco Fichtner p->trailspace = 0;
95780387638SSascha Wildner }
95880387638SSascha Wildner
95980387638SSascha Wildner static int
termp_nm_pre(DECL_ARGS)96080387638SSascha Wildner termp_nm_pre(DECL_ARGS)
96180387638SSascha Wildner {
96254ba9607SSascha Wildner const char *cp;
96380387638SSascha Wildner
96454ba9607SSascha Wildner if (n->type == ROFFT_BLOCK) {
9657888c61dSFranco Fichtner p->flags |= TERMP_PREKEEP;
96654ba9607SSascha Wildner return 1;
9677888c61dSFranco Fichtner }
96880387638SSascha Wildner
96954ba9607SSascha Wildner if (n->type == ROFFT_BODY) {
97054ba9607SSascha Wildner if (n->child == NULL)
97154ba9607SSascha Wildner return 0;
97236342e81SSascha Wildner p->flags |= TERMP_NOSPACE;
97354ba9607SSascha Wildner cp = NULL;
97454ba9607SSascha Wildner if (n->prev->child != NULL)
97554ba9607SSascha Wildner cp = n->prev->child->string;
97654ba9607SSascha Wildner if (cp == NULL)
97754ba9607SSascha Wildner cp = meta->name;
97854ba9607SSascha Wildner if (cp == NULL)
97954ba9607SSascha Wildner p->tcol->offset += term_len(p, 6);
98054ba9607SSascha Wildner else
98154ba9607SSascha Wildner p->tcol->offset += term_len(p, 1) +
98254ba9607SSascha Wildner term_strlen(p, cp);
98354ba9607SSascha Wildner return 1;
98480387638SSascha Wildner }
98580387638SSascha Wildner
98654ba9607SSascha Wildner if (n->child == NULL)
98754ba9607SSascha Wildner return 0;
98880387638SSascha Wildner
98954ba9607SSascha Wildner if (n->type == ROFFT_HEAD)
99080387638SSascha Wildner synopsis_pre(p, n->parent);
99180387638SSascha Wildner
99254ba9607SSascha Wildner if (n->type == ROFFT_HEAD &&
99354ba9607SSascha Wildner n->next != NULL && n->next->child != NULL) {
994070c62a6SFranco Fichtner p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
9957888c61dSFranco Fichtner p->trailspace = 1;
99654ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
99754ba9607SSascha Wildner if (n->child == NULL)
99854ba9607SSascha Wildner p->tcol->rmargin += term_strlen(p, meta->name);
99954ba9607SSascha Wildner else if (n->child->type == ROFFT_TEXT) {
100054ba9607SSascha Wildner p->tcol->rmargin += term_strlen(p, n->child->string);
100154ba9607SSascha Wildner if (n->child->next != NULL)
100280387638SSascha Wildner p->flags |= TERMP_HANG;
100380387638SSascha Wildner } else {
100454ba9607SSascha Wildner p->tcol->rmargin += term_len(p, 5);
100580387638SSascha Wildner p->flags |= TERMP_HANG;
100680387638SSascha Wildner }
100780387638SSascha Wildner }
1008*99db7d0eSSascha Wildner return termp_bold_pre(p, pair, meta, n);
100980387638SSascha Wildner }
101080387638SSascha Wildner
101180387638SSascha Wildner static void
termp_nm_post(DECL_ARGS)101280387638SSascha Wildner termp_nm_post(DECL_ARGS)
101380387638SSascha Wildner {
1014*99db7d0eSSascha Wildner switch (n->type) {
1015*99db7d0eSSascha Wildner case ROFFT_BLOCK:
10167888c61dSFranco Fichtner p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1017*99db7d0eSSascha Wildner break;
1018*99db7d0eSSascha Wildner case ROFFT_HEAD:
1019*99db7d0eSSascha Wildner if (n->next == NULL || n->next->child == NULL)
1020*99db7d0eSSascha Wildner break;
102180387638SSascha Wildner term_flushln(p);
1022070c62a6SFranco Fichtner p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
10237888c61dSFranco Fichtner p->trailspace = 0;
1024*99db7d0eSSascha Wildner break;
1025*99db7d0eSSascha Wildner case ROFFT_BODY:
1026*99db7d0eSSascha Wildner if (n->child != NULL)
102780387638SSascha Wildner term_flushln(p);
1028*99db7d0eSSascha Wildner break;
1029*99db7d0eSSascha Wildner default:
1030*99db7d0eSSascha Wildner break;
1031*99db7d0eSSascha Wildner }
103280387638SSascha Wildner }
103380387638SSascha Wildner
103480387638SSascha Wildner static int
termp_fl_pre(DECL_ARGS)103580387638SSascha Wildner termp_fl_pre(DECL_ARGS)
103680387638SSascha Wildner {
1037*99db7d0eSSascha Wildner struct roff_node *nn;
103880387638SSascha Wildner
103980387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
104080387638SSascha Wildner term_word(p, "\\-");
104180387638SSascha Wildner
1042*99db7d0eSSascha Wildner if (n->child != NULL ||
1043*99db7d0eSSascha Wildner ((nn = roff_node_next(n)) != NULL &&
1044*99db7d0eSSascha Wildner nn->type != ROFFT_TEXT &&
1045*99db7d0eSSascha Wildner (nn->flags & NODE_LINE) == 0))
104680387638SSascha Wildner p->flags |= TERMP_NOSPACE;
104780387638SSascha Wildner
104854ba9607SSascha Wildner return 1;
104980387638SSascha Wildner }
105080387638SSascha Wildner
105180387638SSascha Wildner static int
termp__a_pre(DECL_ARGS)105280387638SSascha Wildner termp__a_pre(DECL_ARGS)
105380387638SSascha Wildner {
1054*99db7d0eSSascha Wildner struct roff_node *nn;
105580387638SSascha Wildner
1056*99db7d0eSSascha Wildner if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A &&
1057*99db7d0eSSascha Wildner ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A))
105880387638SSascha Wildner term_word(p, "and");
105980387638SSascha Wildner
106054ba9607SSascha Wildner return 1;
106180387638SSascha Wildner }
106280387638SSascha Wildner
106380387638SSascha Wildner static int
termp_an_pre(DECL_ARGS)106480387638SSascha Wildner termp_an_pre(DECL_ARGS)
106580387638SSascha Wildner {
106680387638SSascha Wildner
106754ba9607SSascha Wildner if (n->norm->An.auth == AUTH_split) {
106880387638SSascha Wildner p->flags &= ~TERMP_NOSPLIT;
106980387638SSascha Wildner p->flags |= TERMP_SPLIT;
107054ba9607SSascha Wildner return 0;
107154ba9607SSascha Wildner }
107254ba9607SSascha Wildner if (n->norm->An.auth == AUTH_nosplit) {
107380387638SSascha Wildner p->flags &= ~TERMP_SPLIT;
107480387638SSascha Wildner p->flags |= TERMP_NOSPLIT;
107554ba9607SSascha Wildner return 0;
107680387638SSascha Wildner }
107780387638SSascha Wildner
107854ba9607SSascha Wildner if (p->flags & TERMP_SPLIT)
107954ba9607SSascha Wildner term_newln(p);
108054ba9607SSascha Wildner
108154ba9607SSascha Wildner if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
108254ba9607SSascha Wildner p->flags |= TERMP_SPLIT;
108354ba9607SSascha Wildner
108454ba9607SSascha Wildner return 1;
108580387638SSascha Wildner }
108680387638SSascha Wildner
108780387638SSascha Wildner static int
termp_ns_pre(DECL_ARGS)108880387638SSascha Wildner termp_ns_pre(DECL_ARGS)
108980387638SSascha Wildner {
109080387638SSascha Wildner
109154ba9607SSascha Wildner if ( ! (NODE_LINE & n->flags))
109280387638SSascha Wildner p->flags |= TERMP_NOSPACE;
109354ba9607SSascha Wildner return 1;
109480387638SSascha Wildner }
109580387638SSascha Wildner
109680387638SSascha Wildner static int
termp_rs_pre(DECL_ARGS)109780387638SSascha Wildner termp_rs_pre(DECL_ARGS)
109880387638SSascha Wildner {
109980387638SSascha Wildner if (SEC_SEE_ALSO != n->sec)
110054ba9607SSascha Wildner return 1;
1101*99db7d0eSSascha Wildner if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL)
110280387638SSascha Wildner term_vspace(p);
110354ba9607SSascha Wildner return 1;
110480387638SSascha Wildner }
110580387638SSascha Wildner
110680387638SSascha Wildner static int
termp_ex_pre(DECL_ARGS)110780387638SSascha Wildner termp_ex_pre(DECL_ARGS)
110880387638SSascha Wildner {
110960e1e752SSascha Wildner term_newln(p);
111054ba9607SSascha Wildner return 1;
111180387638SSascha Wildner }
111280387638SSascha Wildner
111380387638SSascha Wildner static int
termp_nd_pre(DECL_ARGS)111480387638SSascha Wildner termp_nd_pre(DECL_ARGS)
111580387638SSascha Wildner {
111654ba9607SSascha Wildner if (n->type == ROFFT_BODY)
111780387638SSascha Wildner term_word(p, "\\(en");
111854ba9607SSascha Wildner return 1;
111980387638SSascha Wildner }
112080387638SSascha Wildner
112180387638SSascha Wildner static int
termp_bl_pre(DECL_ARGS)112280387638SSascha Wildner termp_bl_pre(DECL_ARGS)
112380387638SSascha Wildner {
1124*99db7d0eSSascha Wildner switch (n->type) {
1125*99db7d0eSSascha Wildner case ROFFT_BLOCK:
1126*99db7d0eSSascha Wildner term_newln(p);
1127*99db7d0eSSascha Wildner return 1;
1128*99db7d0eSSascha Wildner case ROFFT_HEAD:
1129*99db7d0eSSascha Wildner return 0;
1130*99db7d0eSSascha Wildner default:
1131*99db7d0eSSascha Wildner return 1;
1132*99db7d0eSSascha Wildner }
113380387638SSascha Wildner }
113480387638SSascha Wildner
113580387638SSascha Wildner static void
termp_bl_post(DECL_ARGS)113680387638SSascha Wildner termp_bl_post(DECL_ARGS)
113780387638SSascha Wildner {
113854ba9607SSascha Wildner if (n->type != ROFFT_BLOCK)
113954ba9607SSascha Wildner return;
114080387638SSascha Wildner term_newln(p);
114154ba9607SSascha Wildner if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
114254ba9607SSascha Wildner return;
114354ba9607SSascha Wildner term_tab_set(p, NULL);
114454ba9607SSascha Wildner term_tab_set(p, "T");
114554ba9607SSascha Wildner term_tab_set(p, ".5i");
114680387638SSascha Wildner }
114780387638SSascha Wildner
114880387638SSascha Wildner static int
termp_xr_pre(DECL_ARGS)114980387638SSascha Wildner termp_xr_pre(DECL_ARGS)
115080387638SSascha Wildner {
115160e1e752SSascha Wildner if (NULL == (n = n->child))
115254ba9607SSascha Wildner return 0;
115380387638SSascha Wildner
115454ba9607SSascha Wildner assert(n->type == ROFFT_TEXT);
115560e1e752SSascha Wildner term_word(p, n->string);
115680387638SSascha Wildner
115760e1e752SSascha Wildner if (NULL == (n = n->next))
115854ba9607SSascha Wildner return 0;
115960e1e752SSascha Wildner
116080387638SSascha Wildner p->flags |= TERMP_NOSPACE;
116180387638SSascha Wildner term_word(p, "(");
116260e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
116360e1e752SSascha Wildner
116454ba9607SSascha Wildner assert(n->type == ROFFT_TEXT);
116560e1e752SSascha Wildner term_word(p, n->string);
116660e1e752SSascha Wildner
116760e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
116880387638SSascha Wildner term_word(p, ")");
116980387638SSascha Wildner
117054ba9607SSascha Wildner return 0;
117180387638SSascha Wildner }
117280387638SSascha Wildner
117380387638SSascha Wildner /*
117480387638SSascha Wildner * This decides how to assert whitespace before any of the SYNOPSIS set
117580387638SSascha Wildner * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
117680387638SSascha Wildner * macro combos).
117780387638SSascha Wildner */
117880387638SSascha Wildner static void
synopsis_pre(struct termp * p,struct roff_node * n)1179*99db7d0eSSascha Wildner synopsis_pre(struct termp *p, struct roff_node *n)
118080387638SSascha Wildner {
1181*99db7d0eSSascha Wildner struct roff_node *np;
1182*99db7d0eSSascha Wildner
1183*99db7d0eSSascha Wildner if ((n->flags & NODE_SYNPRETTY) == 0 ||
1184*99db7d0eSSascha Wildner (np = roff_node_prev(n)) == NULL)
118580387638SSascha Wildner return;
118680387638SSascha Wildner
118780387638SSascha Wildner /*
118880387638SSascha Wildner * If we're the second in a pair of like elements, emit our
118980387638SSascha Wildner * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
119080387638SSascha Wildner * case we soldier on.
119180387638SSascha Wildner */
1192*99db7d0eSSascha Wildner if (np->tok == n->tok &&
119380387638SSascha Wildner MDOC_Ft != n->tok &&
119480387638SSascha Wildner MDOC_Fo != n->tok &&
119580387638SSascha Wildner MDOC_Fn != n->tok) {
119680387638SSascha Wildner term_newln(p);
119780387638SSascha Wildner return;
119880387638SSascha Wildner }
119980387638SSascha Wildner
120080387638SSascha Wildner /*
120180387638SSascha Wildner * If we're one of the SYNOPSIS set and non-like pair-wise after
120280387638SSascha Wildner * another (or Fn/Fo, which we've let slip through) then assert
120380387638SSascha Wildner * vertical space, else only newline and move on.
120480387638SSascha Wildner */
1205*99db7d0eSSascha Wildner switch (np->tok) {
1206070c62a6SFranco Fichtner case MDOC_Fd:
1207070c62a6SFranco Fichtner case MDOC_Fn:
1208070c62a6SFranco Fichtner case MDOC_Fo:
1209070c62a6SFranco Fichtner case MDOC_In:
1210070c62a6SFranco Fichtner case MDOC_Vt:
121180387638SSascha Wildner term_vspace(p);
121280387638SSascha Wildner break;
1213070c62a6SFranco Fichtner case MDOC_Ft:
1214*99db7d0eSSascha Wildner if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
121580387638SSascha Wildner term_vspace(p);
121680387638SSascha Wildner break;
121780387638SSascha Wildner }
121880387638SSascha Wildner /* FALLTHROUGH */
121980387638SSascha Wildner default:
122080387638SSascha Wildner term_newln(p);
122180387638SSascha Wildner break;
122280387638SSascha Wildner }
122380387638SSascha Wildner }
122480387638SSascha Wildner
122580387638SSascha Wildner static int
termp_vt_pre(DECL_ARGS)122680387638SSascha Wildner termp_vt_pre(DECL_ARGS)
122780387638SSascha Wildner {
1228*99db7d0eSSascha Wildner switch (n->type) {
1229*99db7d0eSSascha Wildner case ROFFT_ELEM:
1230*99db7d0eSSascha Wildner return termp_ft_pre(p, pair, meta, n);
1231*99db7d0eSSascha Wildner case ROFFT_BLOCK:
123280387638SSascha Wildner synopsis_pre(p, n);
123354ba9607SSascha Wildner return 1;
1234*99db7d0eSSascha Wildner case ROFFT_HEAD:
123554ba9607SSascha Wildner return 0;
1236*99db7d0eSSascha Wildner default:
123754ba9607SSascha Wildner return termp_under_pre(p, pair, meta, n);
123880387638SSascha Wildner }
1239*99db7d0eSSascha Wildner }
124080387638SSascha Wildner
124180387638SSascha Wildner static int
termp_bold_pre(DECL_ARGS)124280387638SSascha Wildner termp_bold_pre(DECL_ARGS)
124380387638SSascha Wildner {
124480387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
124554ba9607SSascha Wildner return 1;
124680387638SSascha Wildner }
124780387638SSascha Wildner
124880387638SSascha Wildner static int
termp_fd_pre(DECL_ARGS)124980387638SSascha Wildner termp_fd_pre(DECL_ARGS)
125080387638SSascha Wildner {
125180387638SSascha Wildner synopsis_pre(p, n);
125254ba9607SSascha Wildner return termp_bold_pre(p, pair, meta, n);
1253f88b6c16SFranco Fichtner }
1254f88b6c16SFranco Fichtner
1255f88b6c16SFranco Fichtner static void
termp_fd_post(DECL_ARGS)1256f88b6c16SFranco Fichtner termp_fd_post(DECL_ARGS)
1257f88b6c16SFranco Fichtner {
1258f88b6c16SFranco Fichtner term_newln(p);
125980387638SSascha Wildner }
126080387638SSascha Wildner
126180387638SSascha Wildner static int
termp_sh_pre(DECL_ARGS)126280387638SSascha Wildner termp_sh_pre(DECL_ARGS)
126380387638SSascha Wildner {
1264*99db7d0eSSascha Wildner struct roff_node *np;
126580387638SSascha Wildner
126680387638SSascha Wildner switch (n->type) {
126754ba9607SSascha Wildner case ROFFT_BLOCK:
126854ba9607SSascha Wildner /*
126954ba9607SSascha Wildner * Vertical space before sections, except
127054ba9607SSascha Wildner * when the previous section was empty.
127154ba9607SSascha Wildner */
1272*99db7d0eSSascha Wildner if ((np = roff_node_prev(n)) == NULL ||
1273*99db7d0eSSascha Wildner np->tok != MDOC_Sh ||
1274*99db7d0eSSascha Wildner (np->body != NULL && np->body->child != NULL))
127580387638SSascha Wildner term_vspace(p);
127680387638SSascha Wildner break;
127754ba9607SSascha Wildner case ROFFT_HEAD:
1278*99db7d0eSSascha Wildner return termp_bold_pre(p, pair, meta, n);
127954ba9607SSascha Wildner case ROFFT_BODY:
128054ba9607SSascha Wildner p->tcol->offset = term_len(p, p->defindent);
128154ba9607SSascha Wildner term_tab_set(p, NULL);
128254ba9607SSascha Wildner term_tab_set(p, "T");
128354ba9607SSascha Wildner term_tab_set(p, ".5i");
1284*99db7d0eSSascha Wildner if (n->sec == SEC_AUTHORS)
1285f88b6c16SFranco Fichtner p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
128680387638SSascha Wildner break;
128780387638SSascha Wildner default:
128880387638SSascha Wildner break;
128980387638SSascha Wildner }
129054ba9607SSascha Wildner return 1;
129180387638SSascha Wildner }
129280387638SSascha Wildner
129380387638SSascha Wildner static void
termp_sh_post(DECL_ARGS)129480387638SSascha Wildner termp_sh_post(DECL_ARGS)
129580387638SSascha Wildner {
129680387638SSascha Wildner switch (n->type) {
129754ba9607SSascha Wildner case ROFFT_HEAD:
129880387638SSascha Wildner term_newln(p);
129980387638SSascha Wildner break;
130054ba9607SSascha Wildner case ROFFT_BODY:
130180387638SSascha Wildner term_newln(p);
130254ba9607SSascha Wildner p->tcol->offset = 0;
130380387638SSascha Wildner break;
130480387638SSascha Wildner default:
130580387638SSascha Wildner break;
130680387638SSascha Wildner }
130780387638SSascha Wildner }
130880387638SSascha Wildner
130980387638SSascha Wildner static void
termp_lb_post(DECL_ARGS)131080387638SSascha Wildner termp_lb_post(DECL_ARGS)
131180387638SSascha Wildner {
1312*99db7d0eSSascha Wildner if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE)
131380387638SSascha Wildner term_newln(p);
131480387638SSascha Wildner }
131580387638SSascha Wildner
131680387638SSascha Wildner static int
termp_d1_pre(DECL_ARGS)131780387638SSascha Wildner termp_d1_pre(DECL_ARGS)
131880387638SSascha Wildner {
131954ba9607SSascha Wildner if (n->type != ROFFT_BLOCK)
132054ba9607SSascha Wildner return 1;
132180387638SSascha Wildner term_newln(p);
132254ba9607SSascha Wildner p->tcol->offset += term_len(p, p->defindent + 1);
132354ba9607SSascha Wildner term_tab_set(p, NULL);
132454ba9607SSascha Wildner term_tab_set(p, "T");
132554ba9607SSascha Wildner term_tab_set(p, ".5i");
132654ba9607SSascha Wildner return 1;
132780387638SSascha Wildner }
132880387638SSascha Wildner
132980387638SSascha Wildner static int
termp_ft_pre(DECL_ARGS)133080387638SSascha Wildner termp_ft_pre(DECL_ARGS)
133180387638SSascha Wildner {
133280387638SSascha Wildner synopsis_pre(p, n);
1333*99db7d0eSSascha Wildner return termp_under_pre(p, pair, meta, n);
133480387638SSascha Wildner }
133580387638SSascha Wildner
133680387638SSascha Wildner static int
termp_fn_pre(DECL_ARGS)133780387638SSascha Wildner termp_fn_pre(DECL_ARGS)
133880387638SSascha Wildner {
13397888c61dSFranco Fichtner size_t rmargin = 0;
134060e1e752SSascha Wildner int pretty;
134160e1e752SSascha Wildner
134280387638SSascha Wildner synopsis_pre(p, n);
1343*99db7d0eSSascha Wildner pretty = n->flags & NODE_SYNPRETTY;
1344*99db7d0eSSascha Wildner if ((n = n->child) == NULL)
134554ba9607SSascha Wildner return 0;
134660e1e752SSascha Wildner
13477888c61dSFranco Fichtner if (pretty) {
134854ba9607SSascha Wildner rmargin = p->tcol->rmargin;
134954ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1350070c62a6SFranco Fichtner p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
13517888c61dSFranco Fichtner }
13527888c61dSFranco Fichtner
135354ba9607SSascha Wildner assert(n->type == ROFFT_TEXT);
135480387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
135560e1e752SSascha Wildner term_word(p, n->string);
135680387638SSascha Wildner term_fontpop(p);
135780387638SSascha Wildner
13587888c61dSFranco Fichtner if (pretty) {
13597888c61dSFranco Fichtner term_flushln(p);
1360070c62a6SFranco Fichtner p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
136154ba9607SSascha Wildner p->flags |= TERMP_NOPAD;
136254ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
136354ba9607SSascha Wildner p->tcol->rmargin = rmargin;
13647888c61dSFranco Fichtner }
13657888c61dSFranco Fichtner
136680387638SSascha Wildner p->flags |= TERMP_NOSPACE;
136780387638SSascha Wildner term_word(p, "(");
136860e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
136980387638SSascha Wildner
137060e1e752SSascha Wildner for (n = n->next; n; n = n->next) {
137154ba9607SSascha Wildner assert(n->type == ROFFT_TEXT);
137280387638SSascha Wildner term_fontpush(p, TERMFONT_UNDER);
13737888c61dSFranco Fichtner if (pretty)
13747888c61dSFranco Fichtner p->flags |= TERMP_NBRWORD;
137560e1e752SSascha Wildner term_word(p, n->string);
137680387638SSascha Wildner term_fontpop(p);
137780387638SSascha Wildner
137860e1e752SSascha Wildner if (n->next) {
137960e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
138080387638SSascha Wildner term_word(p, ",");
138180387638SSascha Wildner }
138260e1e752SSascha Wildner }
138380387638SSascha Wildner
138460e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
138580387638SSascha Wildner term_word(p, ")");
138680387638SSascha Wildner
138760e1e752SSascha Wildner if (pretty) {
138860e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
138980387638SSascha Wildner term_word(p, ";");
13907888c61dSFranco Fichtner term_flushln(p);
139160e1e752SSascha Wildner }
139254ba9607SSascha Wildner return 0;
139380387638SSascha Wildner }
139480387638SSascha Wildner
139580387638SSascha Wildner static int
termp_fa_pre(DECL_ARGS)139680387638SSascha Wildner termp_fa_pre(DECL_ARGS)
139780387638SSascha Wildner {
139854ba9607SSascha Wildner const struct roff_node *nn;
139980387638SSascha Wildner
1400*99db7d0eSSascha Wildner if (n->parent->tok != MDOC_Fo)
1401*99db7d0eSSascha Wildner return termp_under_pre(p, pair, meta, n);
140280387638SSascha Wildner
1403*99db7d0eSSascha Wildner for (nn = n->child; nn != NULL; nn = nn->next) {
140480387638SSascha Wildner term_fontpush(p, TERMFONT_UNDER);
14057888c61dSFranco Fichtner p->flags |= TERMP_NBRWORD;
140680387638SSascha Wildner term_word(p, nn->string);
140780387638SSascha Wildner term_fontpop(p);
1408*99db7d0eSSascha Wildner if (nn->next != NULL) {
140960e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
141080387638SSascha Wildner term_word(p, ",");
141180387638SSascha Wildner }
141260e1e752SSascha Wildner }
1413*99db7d0eSSascha Wildner if (n->child != NULL &&
1414*99db7d0eSSascha Wildner (nn = roff_node_next(n)) != NULL &&
1415*99db7d0eSSascha Wildner nn->tok == MDOC_Fa) {
1416*99db7d0eSSascha Wildner p->flags |= TERMP_NOSPACE;
1417*99db7d0eSSascha Wildner term_word(p, ",");
1418*99db7d0eSSascha Wildner }
141954ba9607SSascha Wildner return 0;
142080387638SSascha Wildner }
142180387638SSascha Wildner
142280387638SSascha Wildner static int
termp_bd_pre(DECL_ARGS)142380387638SSascha Wildner termp_bd_pre(DECL_ARGS)
142480387638SSascha Wildner {
142554ba9607SSascha Wildner int offset;
142680387638SSascha Wildner
142754ba9607SSascha Wildner if (n->type == ROFFT_BLOCK) {
142880387638SSascha Wildner print_bvspace(p, n, n);
142954ba9607SSascha Wildner return 1;
143054ba9607SSascha Wildner } else if (n->type == ROFFT_HEAD)
143154ba9607SSascha Wildner return 0;
143280387638SSascha Wildner
143354ba9607SSascha Wildner /* Handle the -offset argument. */
143480387638SSascha Wildner
143554ba9607SSascha Wildner if (n->norm->Bd.offs == NULL ||
143654ba9607SSascha Wildner ! strcmp(n->norm->Bd.offs, "left"))
143754ba9607SSascha Wildner /* nothing */;
143854ba9607SSascha Wildner else if ( ! strcmp(n->norm->Bd.offs, "indent"))
143954ba9607SSascha Wildner p->tcol->offset += term_len(p, p->defindent + 1);
144054ba9607SSascha Wildner else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
144154ba9607SSascha Wildner p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
144254ba9607SSascha Wildner else {
144354ba9607SSascha Wildner offset = a2width(p, n->norm->Bd.offs);
144454ba9607SSascha Wildner if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
144554ba9607SSascha Wildner p->tcol->offset = 0;
144654ba9607SSascha Wildner else if (offset < SHRT_MAX)
144754ba9607SSascha Wildner p->tcol->offset += offset;
1448070c62a6SFranco Fichtner }
144954ba9607SSascha Wildner
145054ba9607SSascha Wildner switch (n->norm->Bd.type) {
145154ba9607SSascha Wildner case DISP_literal:
145254ba9607SSascha Wildner term_tab_set(p, NULL);
145354ba9607SSascha Wildner term_tab_set(p, "T");
145454ba9607SSascha Wildner term_tab_set(p, "8n");
145554ba9607SSascha Wildner break;
145654ba9607SSascha Wildner case DISP_centered:
145754ba9607SSascha Wildner p->flags |= TERMP_CENTER;
145854ba9607SSascha Wildner break;
145980387638SSascha Wildner default:
146080387638SSascha Wildner break;
146180387638SSascha Wildner }
146254ba9607SSascha Wildner return 1;
146380387638SSascha Wildner }
146480387638SSascha Wildner
146580387638SSascha Wildner static void
termp_bd_post(DECL_ARGS)146680387638SSascha Wildner termp_bd_post(DECL_ARGS)
146780387638SSascha Wildner {
146854ba9607SSascha Wildner if (n->type != ROFFT_BODY)
146980387638SSascha Wildner return;
147054ba9607SSascha Wildner if (n->norm->Bd.type == DISP_unfilled ||
147154ba9607SSascha Wildner n->norm->Bd.type == DISP_literal)
147254ba9607SSascha Wildner p->flags |= TERMP_BRNEVER;
147380387638SSascha Wildner p->flags |= TERMP_NOSPACE;
147480387638SSascha Wildner term_newln(p);
147554ba9607SSascha Wildner p->flags &= ~TERMP_BRNEVER;
147654ba9607SSascha Wildner if (n->norm->Bd.type == DISP_centered)
147754ba9607SSascha Wildner p->flags &= ~TERMP_CENTER;
147880387638SSascha Wildner }
147980387638SSascha Wildner
148080387638SSascha Wildner static int
termp_xx_pre(DECL_ARGS)148180387638SSascha Wildner termp_xx_pre(DECL_ARGS)
148280387638SSascha Wildner {
148354ba9607SSascha Wildner if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
148454ba9607SSascha Wildner p->flags |= TERMP_PREKEEP;
148554ba9607SSascha Wildner return 1;
148680387638SSascha Wildner }
148780387638SSascha Wildner
148854ba9607SSascha Wildner static void
termp_xx_post(DECL_ARGS)148954ba9607SSascha Wildner termp_xx_post(DECL_ARGS)
149054ba9607SSascha Wildner {
149154ba9607SSascha Wildner if (n->aux == 0)
149254ba9607SSascha Wildner p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
149380387638SSascha Wildner }
149480387638SSascha Wildner
149580387638SSascha Wildner static void
termp_pf_post(DECL_ARGS)149680387638SSascha Wildner termp_pf_post(DECL_ARGS)
149780387638SSascha Wildner {
1498*99db7d0eSSascha Wildner if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
149980387638SSascha Wildner p->flags |= TERMP_NOSPACE;
150080387638SSascha Wildner }
150180387638SSascha Wildner
150280387638SSascha Wildner static int
termp_ss_pre(DECL_ARGS)150380387638SSascha Wildner termp_ss_pre(DECL_ARGS)
150480387638SSascha Wildner {
150580387638SSascha Wildner switch (n->type) {
150654ba9607SSascha Wildner case ROFFT_BLOCK:
1507*99db7d0eSSascha Wildner if (roff_node_prev(n) == NULL)
150880387638SSascha Wildner term_newln(p);
1509*99db7d0eSSascha Wildner else
151080387638SSascha Wildner term_vspace(p);
151180387638SSascha Wildner break;
151254ba9607SSascha Wildner case ROFFT_HEAD:
151354ba9607SSascha Wildner p->tcol->offset = term_len(p, (p->defindent+1)/2);
1514*99db7d0eSSascha Wildner return termp_bold_pre(p, pair, meta, n);
151554ba9607SSascha Wildner case ROFFT_BODY:
151654ba9607SSascha Wildner p->tcol->offset = term_len(p, p->defindent);
151754ba9607SSascha Wildner term_tab_set(p, NULL);
151854ba9607SSascha Wildner term_tab_set(p, "T");
151954ba9607SSascha Wildner term_tab_set(p, ".5i");
1520070c62a6SFranco Fichtner break;
152180387638SSascha Wildner default:
152280387638SSascha Wildner break;
152380387638SSascha Wildner }
152454ba9607SSascha Wildner return 1;
152580387638SSascha Wildner }
152680387638SSascha Wildner
152780387638SSascha Wildner static void
termp_ss_post(DECL_ARGS)152880387638SSascha Wildner termp_ss_post(DECL_ARGS)
152980387638SSascha Wildner {
153054ba9607SSascha Wildner if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
153180387638SSascha Wildner term_newln(p);
153280387638SSascha Wildner }
153380387638SSascha Wildner
153480387638SSascha Wildner static int
termp_in_pre(DECL_ARGS)153580387638SSascha Wildner termp_in_pre(DECL_ARGS)
153680387638SSascha Wildner {
153780387638SSascha Wildner synopsis_pre(p, n);
1538*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) {
153980387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
154080387638SSascha Wildner term_word(p, "#include");
154180387638SSascha Wildner term_word(p, "<");
154280387638SSascha Wildner } else {
154380387638SSascha Wildner term_word(p, "<");
154480387638SSascha Wildner term_fontpush(p, TERMFONT_UNDER);
154580387638SSascha Wildner }
154680387638SSascha Wildner p->flags |= TERMP_NOSPACE;
154754ba9607SSascha Wildner return 1;
154880387638SSascha Wildner }
154980387638SSascha Wildner
155080387638SSascha Wildner static void
termp_in_post(DECL_ARGS)155180387638SSascha Wildner termp_in_post(DECL_ARGS)
155280387638SSascha Wildner {
1553*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY)
155480387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
155580387638SSascha Wildner p->flags |= TERMP_NOSPACE;
155680387638SSascha Wildner term_word(p, ">");
1557*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY)
155880387638SSascha Wildner term_fontpop(p);
155980387638SSascha Wildner }
156080387638SSascha Wildner
156180387638SSascha Wildner static int
termp_pp_pre(DECL_ARGS)156254ba9607SSascha Wildner termp_pp_pre(DECL_ARGS)
156380387638SSascha Wildner {
156480387638SSascha Wildner term_vspace(p);
1565*99db7d0eSSascha Wildner if (n->flags & NODE_ID)
1566*99db7d0eSSascha Wildner term_tag_write(n, p->line);
156754ba9607SSascha Wildner return 0;
156880387638SSascha Wildner }
156980387638SSascha Wildner
1570070c62a6SFranco Fichtner static int
termp_skip_pre(DECL_ARGS)157154ba9607SSascha Wildner termp_skip_pre(DECL_ARGS)
1572070c62a6SFranco Fichtner {
157354ba9607SSascha Wildner return 0;
1574070c62a6SFranco Fichtner }
1575070c62a6SFranco Fichtner
157680387638SSascha Wildner static int
termp_quote_pre(DECL_ARGS)157780387638SSascha Wildner termp_quote_pre(DECL_ARGS)
157880387638SSascha Wildner {
157954ba9607SSascha Wildner if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
158054ba9607SSascha Wildner return 1;
158180387638SSascha Wildner
158280387638SSascha Wildner switch (n->tok) {
1583070c62a6SFranco Fichtner case MDOC_Ao:
1584070c62a6SFranco Fichtner case MDOC_Aq:
158554ba9607SSascha Wildner term_word(p, n->child != NULL && n->child->next == NULL &&
158654ba9607SSascha Wildner n->child->tok == MDOC_Mt ? "<" : "\\(la");
158780387638SSascha Wildner break;
1588070c62a6SFranco Fichtner case MDOC_Bro:
1589070c62a6SFranco Fichtner case MDOC_Brq:
159080387638SSascha Wildner term_word(p, "{");
159180387638SSascha Wildner break;
1592070c62a6SFranco Fichtner case MDOC_Oo:
1593070c62a6SFranco Fichtner case MDOC_Op:
1594070c62a6SFranco Fichtner case MDOC_Bo:
1595070c62a6SFranco Fichtner case MDOC_Bq:
159680387638SSascha Wildner term_word(p, "[");
159780387638SSascha Wildner break;
159854ba9607SSascha Wildner case MDOC__T:
159980387638SSascha Wildner /* FALLTHROUGH */
160054ba9607SSascha Wildner case MDOC_Do:
1601070c62a6SFranco Fichtner case MDOC_Dq:
1602f88b6c16SFranco Fichtner term_word(p, "\\(lq");
160380387638SSascha Wildner break;
1604070c62a6SFranco Fichtner case MDOC_En:
1605070c62a6SFranco Fichtner if (NULL == n->norm->Es ||
1606070c62a6SFranco Fichtner NULL == n->norm->Es->child)
160754ba9607SSascha Wildner return 1;
1608070c62a6SFranco Fichtner term_word(p, n->norm->Es->child->string);
160936342e81SSascha Wildner break;
1610070c62a6SFranco Fichtner case MDOC_Po:
1611070c62a6SFranco Fichtner case MDOC_Pq:
161280387638SSascha Wildner term_word(p, "(");
161380387638SSascha Wildner break;
1614070c62a6SFranco Fichtner case MDOC_Qo:
1615070c62a6SFranco Fichtner case MDOC_Qq:
161680387638SSascha Wildner term_word(p, "\"");
161780387638SSascha Wildner break;
1618070c62a6SFranco Fichtner case MDOC_Ql:
1619070c62a6SFranco Fichtner case MDOC_So:
1620070c62a6SFranco Fichtner case MDOC_Sq:
1621f88b6c16SFranco Fichtner term_word(p, "\\(oq");
162280387638SSascha Wildner break;
162380387638SSascha Wildner default:
162480387638SSascha Wildner abort();
162580387638SSascha Wildner }
162680387638SSascha Wildner
162780387638SSascha Wildner p->flags |= TERMP_NOSPACE;
162854ba9607SSascha Wildner return 1;
162980387638SSascha Wildner }
163080387638SSascha Wildner
163180387638SSascha Wildner static void
termp_quote_post(DECL_ARGS)163280387638SSascha Wildner termp_quote_post(DECL_ARGS)
163380387638SSascha Wildner {
163480387638SSascha Wildner
163554ba9607SSascha Wildner if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
163680387638SSascha Wildner return;
163780387638SSascha Wildner
163880387638SSascha Wildner p->flags |= TERMP_NOSPACE;
163980387638SSascha Wildner
164080387638SSascha Wildner switch (n->tok) {
1641070c62a6SFranco Fichtner case MDOC_Ao:
1642070c62a6SFranco Fichtner case MDOC_Aq:
164354ba9607SSascha Wildner term_word(p, n->child != NULL && n->child->next == NULL &&
164454ba9607SSascha Wildner n->child->tok == MDOC_Mt ? ">" : "\\(ra");
164580387638SSascha Wildner break;
1646070c62a6SFranco Fichtner case MDOC_Bro:
1647070c62a6SFranco Fichtner case MDOC_Brq:
164880387638SSascha Wildner term_word(p, "}");
164980387638SSascha Wildner break;
1650070c62a6SFranco Fichtner case MDOC_Oo:
1651070c62a6SFranco Fichtner case MDOC_Op:
1652070c62a6SFranco Fichtner case MDOC_Bo:
1653070c62a6SFranco Fichtner case MDOC_Bq:
165480387638SSascha Wildner term_word(p, "]");
165580387638SSascha Wildner break;
165654ba9607SSascha Wildner case MDOC__T:
165780387638SSascha Wildner /* FALLTHROUGH */
165854ba9607SSascha Wildner case MDOC_Do:
1659070c62a6SFranco Fichtner case MDOC_Dq:
1660f88b6c16SFranco Fichtner term_word(p, "\\(rq");
166180387638SSascha Wildner break;
1662070c62a6SFranco Fichtner case MDOC_En:
166354ba9607SSascha Wildner if (n->norm->Es == NULL ||
166454ba9607SSascha Wildner n->norm->Es->child == NULL ||
166554ba9607SSascha Wildner n->norm->Es->child->next == NULL)
166654ba9607SSascha Wildner p->flags &= ~TERMP_NOSPACE;
166754ba9607SSascha Wildner else
1668070c62a6SFranco Fichtner term_word(p, n->norm->Es->child->next->string);
1669070c62a6SFranco Fichtner break;
1670070c62a6SFranco Fichtner case MDOC_Po:
1671070c62a6SFranco Fichtner case MDOC_Pq:
167280387638SSascha Wildner term_word(p, ")");
167380387638SSascha Wildner break;
1674070c62a6SFranco Fichtner case MDOC_Qo:
1675070c62a6SFranco Fichtner case MDOC_Qq:
167680387638SSascha Wildner term_word(p, "\"");
167780387638SSascha Wildner break;
1678070c62a6SFranco Fichtner case MDOC_Ql:
1679070c62a6SFranco Fichtner case MDOC_So:
1680070c62a6SFranco Fichtner case MDOC_Sq:
1681f88b6c16SFranco Fichtner term_word(p, "\\(cq");
168280387638SSascha Wildner break;
168380387638SSascha Wildner default:
168480387638SSascha Wildner abort();
168580387638SSascha Wildner }
168680387638SSascha Wildner }
168780387638SSascha Wildner
168880387638SSascha Wildner static int
termp_eo_pre(DECL_ARGS)168954ba9607SSascha Wildner termp_eo_pre(DECL_ARGS)
169054ba9607SSascha Wildner {
169154ba9607SSascha Wildner
169254ba9607SSascha Wildner if (n->type != ROFFT_BODY)
169354ba9607SSascha Wildner return 1;
169454ba9607SSascha Wildner
169554ba9607SSascha Wildner if (n->end == ENDBODY_NOT &&
169654ba9607SSascha Wildner n->parent->head->child == NULL &&
169754ba9607SSascha Wildner n->child != NULL &&
169854ba9607SSascha Wildner n->child->end != ENDBODY_NOT)
169954ba9607SSascha Wildner term_word(p, "\\&");
170054ba9607SSascha Wildner else if (n->end != ENDBODY_NOT ? n->child != NULL :
170154ba9607SSascha Wildner n->parent->head->child != NULL && (n->child != NULL ||
170254ba9607SSascha Wildner (n->parent->tail != NULL && n->parent->tail->child != NULL)))
170354ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
170454ba9607SSascha Wildner
170554ba9607SSascha Wildner return 1;
170654ba9607SSascha Wildner }
170754ba9607SSascha Wildner
170854ba9607SSascha Wildner static void
termp_eo_post(DECL_ARGS)170954ba9607SSascha Wildner termp_eo_post(DECL_ARGS)
171054ba9607SSascha Wildner {
171154ba9607SSascha Wildner int body, tail;
171254ba9607SSascha Wildner
171354ba9607SSascha Wildner if (n->type != ROFFT_BODY)
171454ba9607SSascha Wildner return;
171554ba9607SSascha Wildner
171654ba9607SSascha Wildner if (n->end != ENDBODY_NOT) {
171754ba9607SSascha Wildner p->flags &= ~TERMP_NOSPACE;
171854ba9607SSascha Wildner return;
171954ba9607SSascha Wildner }
172054ba9607SSascha Wildner
172154ba9607SSascha Wildner body = n->child != NULL || n->parent->head->child != NULL;
172254ba9607SSascha Wildner tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
172354ba9607SSascha Wildner
172454ba9607SSascha Wildner if (body && tail)
172554ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
172654ba9607SSascha Wildner else if ( ! (body || tail))
172754ba9607SSascha Wildner term_word(p, "\\&");
172854ba9607SSascha Wildner else if ( ! tail)
172954ba9607SSascha Wildner p->flags &= ~TERMP_NOSPACE;
173054ba9607SSascha Wildner }
173154ba9607SSascha Wildner
173254ba9607SSascha Wildner static int
termp_fo_pre(DECL_ARGS)173380387638SSascha Wildner termp_fo_pre(DECL_ARGS)
173480387638SSascha Wildner {
1735*99db7d0eSSascha Wildner size_t rmargin;
17367888c61dSFranco Fichtner
1737*99db7d0eSSascha Wildner switch (n->type) {
1738*99db7d0eSSascha Wildner case ROFFT_BLOCK:
173980387638SSascha Wildner synopsis_pre(p, n);
174054ba9607SSascha Wildner return 1;
1741*99db7d0eSSascha Wildner case ROFFT_BODY:
174254ba9607SSascha Wildner rmargin = p->tcol->rmargin;
1743*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY) {
174454ba9607SSascha Wildner p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1745070c62a6SFranco Fichtner p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1746070c62a6SFranco Fichtner TERMP_HANG;
17477888c61dSFranco Fichtner }
174880387638SSascha Wildner p->flags |= TERMP_NOSPACE;
174980387638SSascha Wildner term_word(p, "(");
175060e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
1751*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY) {
17527888c61dSFranco Fichtner term_flushln(p);
1753070c62a6SFranco Fichtner p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1754070c62a6SFranco Fichtner TERMP_HANG);
175554ba9607SSascha Wildner p->flags |= TERMP_NOPAD;
175654ba9607SSascha Wildner p->tcol->offset = p->tcol->rmargin;
175754ba9607SSascha Wildner p->tcol->rmargin = rmargin;
17587888c61dSFranco Fichtner }
175954ba9607SSascha Wildner return 1;
1760*99db7d0eSSascha Wildner default:
1761*99db7d0eSSascha Wildner return termp_bold_pre(p, pair, meta, n);
176280387638SSascha Wildner }
176380387638SSascha Wildner }
176480387638SSascha Wildner
176580387638SSascha Wildner static void
termp_fo_post(DECL_ARGS)176680387638SSascha Wildner termp_fo_post(DECL_ARGS)
176780387638SSascha Wildner {
176854ba9607SSascha Wildner if (n->type != ROFFT_BODY)
176980387638SSascha Wildner return;
177080387638SSascha Wildner
177160e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
177280387638SSascha Wildner term_word(p, ")");
177380387638SSascha Wildner
1774*99db7d0eSSascha Wildner if (n->flags & NODE_SYNPRETTY) {
177560e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
177680387638SSascha Wildner term_word(p, ";");
17777888c61dSFranco Fichtner term_flushln(p);
177880387638SSascha Wildner }
177960e1e752SSascha Wildner }
178080387638SSascha Wildner
178180387638SSascha Wildner static int
termp_bf_pre(DECL_ARGS)178280387638SSascha Wildner termp_bf_pre(DECL_ARGS)
178380387638SSascha Wildner {
1784*99db7d0eSSascha Wildner switch (n->type) {
1785*99db7d0eSSascha Wildner case ROFFT_HEAD:
178654ba9607SSascha Wildner return 0;
1787*99db7d0eSSascha Wildner case ROFFT_BODY:
1788*99db7d0eSSascha Wildner break;
1789*99db7d0eSSascha Wildner default:
179054ba9607SSascha Wildner return 1;
1791*99db7d0eSSascha Wildner }
1792*99db7d0eSSascha Wildner switch (n->norm->Bf.font) {
1793*99db7d0eSSascha Wildner case FONT_Em:
1794*99db7d0eSSascha Wildner return termp_under_pre(p, pair, meta, n);
1795*99db7d0eSSascha Wildner case FONT_Sy:
1796*99db7d0eSSascha Wildner return termp_bold_pre(p, pair, meta, n);
1797*99db7d0eSSascha Wildner default:
1798*99db7d0eSSascha Wildner return termp_li_pre(p, pair, meta, n);
1799*99db7d0eSSascha Wildner }
180080387638SSascha Wildner }
180180387638SSascha Wildner
180280387638SSascha Wildner static int
termp_sm_pre(DECL_ARGS)180380387638SSascha Wildner termp_sm_pre(DECL_ARGS)
180480387638SSascha Wildner {
1805*99db7d0eSSascha Wildner if (n->child == NULL)
1806070c62a6SFranco Fichtner p->flags ^= TERMP_NONOSPACE;
1807*99db7d0eSSascha Wildner else if (strcmp(n->child->string, "on") == 0)
180880387638SSascha Wildner p->flags &= ~TERMP_NONOSPACE;
1809070c62a6SFranco Fichtner else
181080387638SSascha Wildner p->flags |= TERMP_NONOSPACE;
181180387638SSascha Wildner
1812070c62a6SFranco Fichtner if (p->col && ! (TERMP_NONOSPACE & p->flags))
1813070c62a6SFranco Fichtner p->flags &= ~TERMP_NOSPACE;
1814070c62a6SFranco Fichtner
181554ba9607SSascha Wildner return 0;
181680387638SSascha Wildner }
181780387638SSascha Wildner
181880387638SSascha Wildner static int
termp_ap_pre(DECL_ARGS)181980387638SSascha Wildner termp_ap_pre(DECL_ARGS)
182080387638SSascha Wildner {
182180387638SSascha Wildner p->flags |= TERMP_NOSPACE;
182280387638SSascha Wildner term_word(p, "'");
182380387638SSascha Wildner p->flags |= TERMP_NOSPACE;
182454ba9607SSascha Wildner return 1;
182580387638SSascha Wildner }
182680387638SSascha Wildner
182780387638SSascha Wildner static void
termp____post(DECL_ARGS)182880387638SSascha Wildner termp____post(DECL_ARGS)
182980387638SSascha Wildner {
1830*99db7d0eSSascha Wildner struct roff_node *nn;
183180387638SSascha Wildner
183280387638SSascha Wildner /*
183380387638SSascha Wildner * Handle lists of authors. In general, print each followed by
183480387638SSascha Wildner * a comma. Don't print the comma if there are only two
183580387638SSascha Wildner * authors.
183680387638SSascha Wildner */
1837*99db7d0eSSascha Wildner if (n->tok == MDOC__A &&
1838*99db7d0eSSascha Wildner (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A &&
1839*99db7d0eSSascha Wildner ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) &&
1840*99db7d0eSSascha Wildner ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A))
184180387638SSascha Wildner return;
184280387638SSascha Wildner
184380387638SSascha Wildner /* TODO: %U. */
184480387638SSascha Wildner
1845*99db7d0eSSascha Wildner if (n->parent == NULL || n->parent->tok != MDOC_Rs)
184680387638SSascha Wildner return;
184780387638SSascha Wildner
184860e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
1849*99db7d0eSSascha Wildner if (roff_node_next(n) == NULL) {
185080387638SSascha Wildner term_word(p, ".");
185180387638SSascha Wildner p->flags |= TERMP_SENTENCE;
185280387638SSascha Wildner } else
185380387638SSascha Wildner term_word(p, ",");
185480387638SSascha Wildner }
185580387638SSascha Wildner
185680387638SSascha Wildner static int
termp_li_pre(DECL_ARGS)185780387638SSascha Wildner termp_li_pre(DECL_ARGS)
185880387638SSascha Wildner {
185980387638SSascha Wildner term_fontpush(p, TERMFONT_NONE);
186054ba9607SSascha Wildner return 1;
186180387638SSascha Wildner }
186280387638SSascha Wildner
186380387638SSascha Wildner static int
termp_lk_pre(DECL_ARGS)186480387638SSascha Wildner termp_lk_pre(DECL_ARGS)
186580387638SSascha Wildner {
186654ba9607SSascha Wildner const struct roff_node *link, *descr, *punct;
186780387638SSascha Wildner
186854ba9607SSascha Wildner if ((link = n->child) == NULL)
186954ba9607SSascha Wildner return 0;
1870f88b6c16SFranco Fichtner
187154ba9607SSascha Wildner /* Find beginning of trailing punctuation. */
187254ba9607SSascha Wildner punct = n->last;
187354ba9607SSascha Wildner while (punct != link && punct->flags & NODE_DELIMC)
187454ba9607SSascha Wildner punct = punct->prev;
187554ba9607SSascha Wildner punct = punct->next;
187654ba9607SSascha Wildner
187754ba9607SSascha Wildner /* Link text. */
187854ba9607SSascha Wildner if ((descr = link->next) != NULL && descr != punct) {
187980387638SSascha Wildner term_fontpush(p, TERMFONT_UNDER);
188054ba9607SSascha Wildner while (descr != punct) {
188154ba9607SSascha Wildner if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
188254ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
1883f88b6c16SFranco Fichtner term_word(p, descr->string);
1884f88b6c16SFranco Fichtner descr = descr->next;
1885f88b6c16SFranco Fichtner }
188654ba9607SSascha Wildner term_fontpop(p);
188760e1e752SSascha Wildner p->flags |= TERMP_NOSPACE;
188880387638SSascha Wildner term_word(p, ":");
1889f88b6c16SFranco Fichtner }
189080387638SSascha Wildner
189154ba9607SSascha Wildner /* Link target. */
189280387638SSascha Wildner term_fontpush(p, TERMFONT_BOLD);
1893f88b6c16SFranco Fichtner term_word(p, link->string);
189480387638SSascha Wildner term_fontpop(p);
189580387638SSascha Wildner
189654ba9607SSascha Wildner /* Trailing punctuation. */
189754ba9607SSascha Wildner while (punct != NULL) {
189854ba9607SSascha Wildner p->flags |= TERMP_NOSPACE;
189954ba9607SSascha Wildner term_word(p, punct->string);
190054ba9607SSascha Wildner punct = punct->next;
190154ba9607SSascha Wildner }
190254ba9607SSascha Wildner return 0;
190380387638SSascha Wildner }
190480387638SSascha Wildner
190580387638SSascha Wildner static int
termp_bk_pre(DECL_ARGS)190680387638SSascha Wildner termp_bk_pre(DECL_ARGS)
190780387638SSascha Wildner {
190880387638SSascha Wildner switch (n->type) {
190954ba9607SSascha Wildner case ROFFT_BLOCK:
191080387638SSascha Wildner break;
191154ba9607SSascha Wildner case ROFFT_HEAD:
191254ba9607SSascha Wildner return 0;
191354ba9607SSascha Wildner case ROFFT_BODY:
191454ba9607SSascha Wildner if (n->parent->args != NULL || n->prev->child == NULL)
191580387638SSascha Wildner p->flags |= TERMP_PREKEEP;
191680387638SSascha Wildner break;
191780387638SSascha Wildner default:
191880387638SSascha Wildner abort();
191980387638SSascha Wildner }
192054ba9607SSascha Wildner return 1;
192180387638SSascha Wildner }
192280387638SSascha Wildner
192380387638SSascha Wildner static void
termp_bk_post(DECL_ARGS)192480387638SSascha Wildner termp_bk_post(DECL_ARGS)
192580387638SSascha Wildner {
192654ba9607SSascha Wildner if (n->type == ROFFT_BODY)
192780387638SSascha Wildner p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
192880387638SSascha Wildner }
192980387638SSascha Wildner
1930*99db7d0eSSascha Wildner /*
1931*99db7d0eSSascha Wildner * If we are in an `Rs' and there is a journal present,
1932*99db7d0eSSascha Wildner * then quote us instead of underlining us (for disambiguation).
1933*99db7d0eSSascha Wildner */
193480387638SSascha Wildner static void
termp__t_post(DECL_ARGS)193580387638SSascha Wildner termp__t_post(DECL_ARGS)
193680387638SSascha Wildner {
1937*99db7d0eSSascha Wildner if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
193860e1e752SSascha Wildner n->parent->norm->Rs.quote_T)
1939f88b6c16SFranco Fichtner termp_quote_post(p, pair, meta, n);
1940f88b6c16SFranco Fichtner termp____post(p, pair, meta, n);
194180387638SSascha Wildner }
194280387638SSascha Wildner
194380387638SSascha Wildner static int
termp__t_pre(DECL_ARGS)194480387638SSascha Wildner termp__t_pre(DECL_ARGS)
194580387638SSascha Wildner {
1946*99db7d0eSSascha Wildner if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
194760e1e752SSascha Wildner n->parent->norm->Rs.quote_T)
194854ba9607SSascha Wildner return termp_quote_pre(p, pair, meta, n);
1949*99db7d0eSSascha Wildner else
1950*99db7d0eSSascha Wildner return termp_under_pre(p, pair, meta, n);
195180387638SSascha Wildner }
195280387638SSascha Wildner
195380387638SSascha Wildner static int
termp_under_pre(DECL_ARGS)195480387638SSascha Wildner termp_under_pre(DECL_ARGS)
195580387638SSascha Wildner {
195680387638SSascha Wildner term_fontpush(p, TERMFONT_UNDER);
195754ba9607SSascha Wildner return 1;
195854ba9607SSascha Wildner }
195954ba9607SSascha Wildner
196054ba9607SSascha Wildner static int
termp_abort_pre(DECL_ARGS)196154ba9607SSascha Wildner termp_abort_pre(DECL_ARGS)
196254ba9607SSascha Wildner {
196354ba9607SSascha Wildner abort();
196480387638SSascha Wildner }
1965