xref: /dflybsd-src/contrib/mdocml/mdoc_term.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
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