xref: /illumos-gate/usr/src/cmd/mandoc/mdoc_term.c (revision 4d131170)
1*4d131170SRobert Mustacchi /* $Id: mdoc_term.c,v 1.380 2020/04/06 10:16:17 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*4d131170SRobert Mustacchi  * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
495c635efSGarrett D'Amore  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5698f87a4SGarrett D'Amore  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
695c635efSGarrett D'Amore  *
795c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
895c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
995c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1095c635efSGarrett D'Amore  *
11371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1295c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1495c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1595c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1695c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1795c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*4d131170SRobert Mustacchi  *
19*4d131170SRobert Mustacchi  * Plain text formatter for mdoc(7), used by mandoc(1)
20*4d131170SRobert Mustacchi  * for ASCII, UTF-8, PostScript, and PDF output.
2195c635efSGarrett D'Amore  */
2295c635efSGarrett D'Amore #include "config.h"
2395c635efSGarrett D'Amore 
2495c635efSGarrett D'Amore #include <sys/types.h>
2595c635efSGarrett D'Amore 
2695c635efSGarrett D'Amore #include <assert.h>
2795c635efSGarrett D'Amore #include <ctype.h>
28260e9a87SYuri Pankov #include <limits.h>
2995c635efSGarrett D'Amore #include <stdint.h>
3095c635efSGarrett D'Amore #include <stdio.h>
3195c635efSGarrett D'Amore #include <stdlib.h>
3295c635efSGarrett D'Amore #include <string.h>
3395c635efSGarrett D'Amore 
34260e9a87SYuri Pankov #include "mandoc_aux.h"
35371584c2SYuri Pankov #include "roff.h"
36371584c2SYuri Pankov #include "mdoc.h"
3795c635efSGarrett D'Amore #include "out.h"
3895c635efSGarrett D'Amore #include "term.h"
39*4d131170SRobert Mustacchi #include "term_tag.h"
4095c635efSGarrett D'Amore #include "main.h"
4195c635efSGarrett D'Amore 
4295c635efSGarrett D'Amore struct	termpair {
4395c635efSGarrett D'Amore 	struct termpair	 *ppair;
4495c635efSGarrett D'Amore 	int		  count;
4595c635efSGarrett D'Amore };
4695c635efSGarrett D'Amore 
4795c635efSGarrett D'Amore #define	DECL_ARGS struct termp *p, \
4895c635efSGarrett D'Amore 		  struct termpair *pair, \
49371584c2SYuri Pankov 		  const struct roff_meta *meta, \
50371584c2SYuri Pankov 		  struct roff_node *n
5195c635efSGarrett D'Amore 
52cec8643bSMichal Nowak struct	mdoc_term_act {
5395c635efSGarrett D'Amore 	int	(*pre)(DECL_ARGS);
5495c635efSGarrett D'Amore 	void	(*post)(DECL_ARGS);
5595c635efSGarrett D'Amore };
5695c635efSGarrett D'Amore 
57260e9a87SYuri Pankov static	int	  a2width(const struct termp *, const char *);
5895c635efSGarrett D'Amore 
5995c635efSGarrett D'Amore static	void	  print_bvspace(struct termp *,
60*4d131170SRobert Mustacchi 			struct roff_node *, struct roff_node *);
61260e9a87SYuri Pankov static	void	  print_mdoc_node(DECL_ARGS);
6295c635efSGarrett D'Amore static	void	  print_mdoc_nodelist(DECL_ARGS);
63371584c2SYuri Pankov static	void	  print_mdoc_head(struct termp *, const struct roff_meta *);
64371584c2SYuri Pankov static	void	  print_mdoc_foot(struct termp *, const struct roff_meta *);
65*4d131170SRobert Mustacchi static	void	  synopsis_pre(struct termp *, struct roff_node *);
6695c635efSGarrett D'Amore 
6795c635efSGarrett D'Amore static	void	  termp____post(DECL_ARGS);
6895c635efSGarrett D'Amore static	void	  termp__t_post(DECL_ARGS);
6995c635efSGarrett D'Amore static	void	  termp_bd_post(DECL_ARGS);
7095c635efSGarrett D'Amore static	void	  termp_bk_post(DECL_ARGS);
7195c635efSGarrett D'Amore static	void	  termp_bl_post(DECL_ARGS);
72260e9a87SYuri Pankov static	void	  termp_eo_post(DECL_ARGS);
73698f87a4SGarrett D'Amore static	void	  termp_fd_post(DECL_ARGS);
7495c635efSGarrett D'Amore static	void	  termp_fo_post(DECL_ARGS);
7595c635efSGarrett D'Amore static	void	  termp_in_post(DECL_ARGS);
7695c635efSGarrett D'Amore static	void	  termp_it_post(DECL_ARGS);
7795c635efSGarrett D'Amore static	void	  termp_lb_post(DECL_ARGS);
7895c635efSGarrett D'Amore static	void	  termp_nm_post(DECL_ARGS);
7995c635efSGarrett D'Amore static	void	  termp_pf_post(DECL_ARGS);
8095c635efSGarrett D'Amore static	void	  termp_quote_post(DECL_ARGS);
8195c635efSGarrett D'Amore static	void	  termp_sh_post(DECL_ARGS);
8295c635efSGarrett D'Amore static	void	  termp_ss_post(DECL_ARGS);
83a40ea1a7SYuri Pankov static	void	  termp_xx_post(DECL_ARGS);
8495c635efSGarrett D'Amore 
8595c635efSGarrett D'Amore static	int	  termp__a_pre(DECL_ARGS);
8695c635efSGarrett D'Amore static	int	  termp__t_pre(DECL_ARGS);
87cec8643bSMichal Nowak static	int	  termp_abort_pre(DECL_ARGS);
8895c635efSGarrett D'Amore static	int	  termp_an_pre(DECL_ARGS);
8995c635efSGarrett D'Amore static	int	  termp_ap_pre(DECL_ARGS);
9095c635efSGarrett D'Amore static	int	  termp_bd_pre(DECL_ARGS);
9195c635efSGarrett D'Amore static	int	  termp_bf_pre(DECL_ARGS);
9295c635efSGarrett D'Amore static	int	  termp_bk_pre(DECL_ARGS);
9395c635efSGarrett D'Amore static	int	  termp_bl_pre(DECL_ARGS);
9495c635efSGarrett D'Amore static	int	  termp_bold_pre(DECL_ARGS);
9595c635efSGarrett D'Amore static	int	  termp_d1_pre(DECL_ARGS);
96260e9a87SYuri Pankov static	int	  termp_eo_pre(DECL_ARGS);
9795c635efSGarrett D'Amore static	int	  termp_ex_pre(DECL_ARGS);
9895c635efSGarrett D'Amore static	int	  termp_fa_pre(DECL_ARGS);
9995c635efSGarrett D'Amore static	int	  termp_fd_pre(DECL_ARGS);
10095c635efSGarrett D'Amore static	int	  termp_fl_pre(DECL_ARGS);
10195c635efSGarrett D'Amore static	int	  termp_fn_pre(DECL_ARGS);
10295c635efSGarrett D'Amore static	int	  termp_fo_pre(DECL_ARGS);
10395c635efSGarrett D'Amore static	int	  termp_ft_pre(DECL_ARGS);
10495c635efSGarrett D'Amore static	int	  termp_in_pre(DECL_ARGS);
10595c635efSGarrett D'Amore static	int	  termp_it_pre(DECL_ARGS);
10695c635efSGarrett D'Amore static	int	  termp_li_pre(DECL_ARGS);
10795c635efSGarrett D'Amore static	int	  termp_lk_pre(DECL_ARGS);
10895c635efSGarrett D'Amore static	int	  termp_nd_pre(DECL_ARGS);
10995c635efSGarrett D'Amore static	int	  termp_nm_pre(DECL_ARGS);
11095c635efSGarrett D'Amore static	int	  termp_ns_pre(DECL_ARGS);
11195c635efSGarrett D'Amore static	int	  termp_quote_pre(DECL_ARGS);
11295c635efSGarrett D'Amore static	int	  termp_rs_pre(DECL_ARGS);
11395c635efSGarrett D'Amore static	int	  termp_sh_pre(DECL_ARGS);
114260e9a87SYuri Pankov static	int	  termp_skip_pre(DECL_ARGS);
11595c635efSGarrett D'Amore static	int	  termp_sm_pre(DECL_ARGS);
116c66b8046SYuri Pankov static	int	  termp_pp_pre(DECL_ARGS);
11795c635efSGarrett D'Amore static	int	  termp_ss_pre(DECL_ARGS);
11895c635efSGarrett D'Amore static	int	  termp_under_pre(DECL_ARGS);
11995c635efSGarrett D'Amore static	int	  termp_vt_pre(DECL_ARGS);
12095c635efSGarrett D'Amore static	int	  termp_xr_pre(DECL_ARGS);
12195c635efSGarrett D'Amore static	int	  termp_xx_pre(DECL_ARGS);
12295c635efSGarrett D'Amore 
123cec8643bSMichal Nowak static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
12495c635efSGarrett D'Amore 	{ NULL, NULL }, /* Dd */
12595c635efSGarrett D'Amore 	{ NULL, NULL }, /* Dt */
12695c635efSGarrett D'Amore 	{ NULL, NULL }, /* Os */
12795c635efSGarrett D'Amore 	{ termp_sh_pre, termp_sh_post }, /* Sh */
128260e9a87SYuri Pankov 	{ termp_ss_pre, termp_ss_post }, /* Ss */
129c66b8046SYuri Pankov 	{ termp_pp_pre, NULL }, /* Pp */
130698f87a4SGarrett D'Amore 	{ termp_d1_pre, termp_bl_post }, /* D1 */
131698f87a4SGarrett D'Amore 	{ termp_d1_pre, termp_bl_post }, /* Dl */
13295c635efSGarrett D'Amore 	{ termp_bd_pre, termp_bd_post }, /* Bd */
13395c635efSGarrett D'Amore 	{ NULL, NULL }, /* Ed */
13495c635efSGarrett D'Amore 	{ termp_bl_pre, termp_bl_post }, /* Bl */
13595c635efSGarrett D'Amore 	{ NULL, NULL }, /* El */
13695c635efSGarrett D'Amore 	{ termp_it_pre, termp_it_post }, /* It */
137260e9a87SYuri Pankov 	{ termp_under_pre, NULL }, /* Ad */
138260e9a87SYuri Pankov 	{ termp_an_pre, NULL }, /* An */
139c66b8046SYuri Pankov 	{ termp_ap_pre, NULL }, /* Ap */
14095c635efSGarrett D'Amore 	{ termp_under_pre, NULL }, /* Ar */
141*4d131170SRobert Mustacchi 	{ termp_fd_pre, NULL }, /* Cd */
14295c635efSGarrett D'Amore 	{ termp_bold_pre, NULL }, /* Cm */
143371584c2SYuri Pankov 	{ termp_li_pre, NULL }, /* Dv */
144*4d131170SRobert Mustacchi 	{ NULL, NULL }, /* Er */
145*4d131170SRobert Mustacchi 	{ NULL, NULL }, /* Ev */
14695c635efSGarrett D'Amore 	{ termp_ex_pre, NULL }, /* Ex */
147260e9a87SYuri Pankov 	{ termp_fa_pre, NULL }, /* Fa */
148260e9a87SYuri Pankov 	{ termp_fd_pre, termp_fd_post }, /* Fd */
14995c635efSGarrett D'Amore 	{ termp_fl_pre, NULL }, /* Fl */
150260e9a87SYuri Pankov 	{ termp_fn_pre, NULL }, /* Fn */
151260e9a87SYuri Pankov 	{ termp_ft_pre, NULL }, /* Ft */
152260e9a87SYuri Pankov 	{ termp_bold_pre, NULL }, /* Ic */
153260e9a87SYuri Pankov 	{ termp_in_pre, termp_in_post }, /* In */
15495c635efSGarrett D'Amore 	{ termp_li_pre, NULL }, /* Li */
155260e9a87SYuri Pankov 	{ termp_nd_pre, NULL }, /* Nd */
156260e9a87SYuri Pankov 	{ termp_nm_pre, termp_nm_post }, /* Nm */
15795c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Op */
158cec8643bSMichal Nowak 	{ termp_abort_pre, NULL }, /* Ot */
15995c635efSGarrett D'Amore 	{ termp_under_pre, NULL }, /* Pa */
160a40ea1a7SYuri Pankov 	{ termp_ex_pre, NULL }, /* Rv */
161260e9a87SYuri Pankov 	{ NULL, NULL }, /* St */
16295c635efSGarrett D'Amore 	{ termp_under_pre, NULL }, /* Va */
16395c635efSGarrett D'Amore 	{ termp_vt_pre, NULL }, /* Vt */
16495c635efSGarrett D'Amore 	{ termp_xr_pre, NULL }, /* Xr */
16595c635efSGarrett D'Amore 	{ termp__a_pre, termp____post }, /* %A */
16695c635efSGarrett D'Amore 	{ termp_under_pre, termp____post }, /* %B */
16795c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %D */
16895c635efSGarrett D'Amore 	{ termp_under_pre, termp____post }, /* %I */
16995c635efSGarrett D'Amore 	{ termp_under_pre, termp____post }, /* %J */
17095c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %N */
17195c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %O */
17295c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %P */
17395c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %R */
17495c635efSGarrett D'Amore 	{ termp__t_pre, termp__t_post }, /* %T */
17595c635efSGarrett D'Amore 	{ NULL, termp____post }, /* %V */
17695c635efSGarrett D'Amore 	{ NULL, NULL }, /* Ac */
17795c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Ao */
17895c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Aq */
17995c635efSGarrett D'Amore 	{ NULL, NULL }, /* At */
18095c635efSGarrett D'Amore 	{ NULL, NULL }, /* Bc */
181260e9a87SYuri Pankov 	{ termp_bf_pre, NULL }, /* Bf */
18295c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Bo */
18395c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Bq */
184a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Bsx */
185a40ea1a7SYuri Pankov 	{ NULL, NULL }, /* Bx */
186260e9a87SYuri Pankov 	{ termp_skip_pre, NULL }, /* Db */
18795c635efSGarrett D'Amore 	{ NULL, NULL }, /* Dc */
18895c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Do */
18995c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Dq */
19095c635efSGarrett D'Amore 	{ NULL, NULL }, /* Ec */ /* FIXME: no space */
19195c635efSGarrett D'Amore 	{ NULL, NULL }, /* Ef */
192*4d131170SRobert Mustacchi 	{ termp_under_pre, NULL }, /* Em */
193260e9a87SYuri Pankov 	{ termp_eo_pre, termp_eo_post }, /* Eo */
194a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Fx */
19595c635efSGarrett D'Amore 	{ termp_bold_pre, NULL }, /* Ms */
196260e9a87SYuri Pankov 	{ termp_li_pre, NULL }, /* No */
19795c635efSGarrett D'Amore 	{ termp_ns_pre, NULL }, /* Ns */
198a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Nx */
199a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Ox */
20095c635efSGarrett D'Amore 	{ NULL, NULL }, /* Pc */
201698f87a4SGarrett D'Amore 	{ NULL, termp_pf_post }, /* Pf */
20295c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Po */
20395c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Pq */
20495c635efSGarrett D'Amore 	{ NULL, NULL }, /* Qc */
20595c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Ql */
20695c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Qo */
20795c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Qq */
20895c635efSGarrett D'Amore 	{ NULL, NULL }, /* Re */
20995c635efSGarrett D'Amore 	{ termp_rs_pre, NULL }, /* Rs */
21095c635efSGarrett D'Amore 	{ NULL, NULL }, /* Sc */
21195c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* So */
21295c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Sq */
21395c635efSGarrett D'Amore 	{ termp_sm_pre, NULL }, /* Sm */
21495c635efSGarrett D'Amore 	{ termp_under_pre, NULL }, /* Sx */
215*4d131170SRobert Mustacchi 	{ termp_bold_pre, NULL }, /* Sy */
21695c635efSGarrett D'Amore 	{ NULL, NULL }, /* Tn */
217a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Ux */
21895c635efSGarrett D'Amore 	{ NULL, NULL }, /* Xc */
21995c635efSGarrett D'Amore 	{ NULL, NULL }, /* Xo */
220260e9a87SYuri Pankov 	{ termp_fo_pre, termp_fo_post }, /* Fo */
221260e9a87SYuri Pankov 	{ NULL, NULL }, /* Fc */
22295c635efSGarrett D'Amore 	{ termp_quote_pre, termp_quote_post }, /* Oo */
22395c635efSGarrett D'Amore 	{ NULL, NULL }, /* Oc */
22495c635efSGarrett D'Amore 	{ termp_bk_pre, termp_bk_post }, /* Bk */
22595c635efSGarrett D'Amore 	{ NULL, NULL }, /* Ek */
226a40ea1a7SYuri Pankov 	{ NULL, NULL }, /* Bt */
22795c635efSGarrett D'Amore 	{ NULL, NULL }, /* Hf */
228260e9a87SYuri Pankov 	{ termp_under_pre, NULL }, /* Fr */
229a40ea1a7SYuri Pankov 	{ NULL, NULL }, /* Ud */
23095c635efSGarrett D'Amore 	{ NULL, termp_lb_post }, /* Lb */
231cec8643bSMichal Nowak 	{ termp_abort_pre, NULL }, /* Lp */
232260e9a87SYuri Pankov 	{ termp_lk_pre, NULL }, /* Lk */
233260e9a87SYuri Pankov 	{ termp_under_pre, NULL }, /* Mt */
234260e9a87SYuri Pankov 	{ termp_quote_pre, termp_quote_post }, /* Brq */
235260e9a87SYuri Pankov 	{ termp_quote_pre, termp_quote_post }, /* Bro */
236260e9a87SYuri Pankov 	{ NULL, NULL }, /* Brc */
237260e9a87SYuri Pankov 	{ NULL, termp____post }, /* %C */
238260e9a87SYuri Pankov 	{ termp_skip_pre, NULL }, /* Es */
239260e9a87SYuri Pankov 	{ termp_quote_pre, termp_quote_post }, /* En */
240a40ea1a7SYuri Pankov 	{ termp_xx_pre, termp_xx_post }, /* Dx */
241260e9a87SYuri Pankov 	{ NULL, termp____post }, /* %Q */
242260e9a87SYuri Pankov 	{ NULL, termp____post }, /* %U */
243260e9a87SYuri Pankov 	{ NULL, NULL }, /* Ta */
244*4d131170SRobert Mustacchi 	{ termp_skip_pre, NULL }, /* Tg */
24595c635efSGarrett D'Amore };
24695c635efSGarrett D'Amore 
247c66b8046SYuri Pankov 
24895c635efSGarrett D'Amore void
terminal_mdoc(void * arg,const struct roff_meta * mdoc)249cec8643bSMichal Nowak terminal_mdoc(void *arg, const struct roff_meta *mdoc)
25095c635efSGarrett D'Amore {
251*4d131170SRobert Mustacchi 	struct roff_node	*n, *nn;
25295c635efSGarrett D'Amore 	struct termp		*p;
253a40ea1a7SYuri Pankov 	size_t			 save_defindent;
25495c635efSGarrett D'Amore 
25595c635efSGarrett D'Amore 	p = (struct termp *)arg;
256c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
257c66b8046SYuri Pankov 	term_tab_set(p, NULL);
258c66b8046SYuri Pankov 	term_tab_set(p, "T");
259c66b8046SYuri Pankov 	term_tab_set(p, ".5i");
26095c635efSGarrett D'Amore 
261371584c2SYuri Pankov 	n = mdoc->first->child;
262260e9a87SYuri Pankov 	if (p->synopsisonly) {
263*4d131170SRobert Mustacchi 		for (nn = NULL; n != NULL; n = n->next) {
264*4d131170SRobert Mustacchi 			if (n->tok != MDOC_Sh)
265*4d131170SRobert Mustacchi 				continue;
266*4d131170SRobert Mustacchi 			if (n->sec == SEC_SYNOPSIS)
267260e9a87SYuri Pankov 				break;
268*4d131170SRobert Mustacchi 			if (nn == NULL && n->sec == SEC_NAME)
269*4d131170SRobert Mustacchi 				nn = n;
270260e9a87SYuri Pankov 		}
271*4d131170SRobert Mustacchi 		if (n == NULL)
272*4d131170SRobert Mustacchi 			n = nn;
273*4d131170SRobert Mustacchi 		p->flags |= TERMP_NOSPACE;
274*4d131170SRobert Mustacchi 		if (n != NULL && (n = n->child->next->child) != NULL)
275*4d131170SRobert Mustacchi 			print_mdoc_nodelist(p, NULL, mdoc, n);
276*4d131170SRobert Mustacchi 		term_newln(p);
277260e9a87SYuri Pankov 	} else {
278a40ea1a7SYuri Pankov 		save_defindent = p->defindent;
279260e9a87SYuri Pankov 		if (p->defindent == 0)
280260e9a87SYuri Pankov 			p->defindent = 5;
281cec8643bSMichal Nowak 		term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
2826640c13bSYuri Pankov 		while (n != NULL &&
2836640c13bSYuri Pankov 		    (n->type == ROFFT_COMMENT ||
2846640c13bSYuri Pankov 		     n->flags & NODE_NOPRT))
285a40ea1a7SYuri Pankov 			n = n->next;
286260e9a87SYuri Pankov 		if (n != NULL) {
287260e9a87SYuri Pankov 			if (n->tok != MDOC_Sh)
288260e9a87SYuri Pankov 				term_vspace(p);
289cec8643bSMichal Nowak 			print_mdoc_nodelist(p, NULL, mdoc, n);
290260e9a87SYuri Pankov 		}
291260e9a87SYuri Pankov 		term_end(p);
292a40ea1a7SYuri Pankov 		p->defindent = save_defindent;
293260e9a87SYuri Pankov 	}
29495c635efSGarrett D'Amore }
29595c635efSGarrett D'Amore 
29695c635efSGarrett D'Amore static void
print_mdoc_nodelist(DECL_ARGS)29795c635efSGarrett D'Amore print_mdoc_nodelist(DECL_ARGS)
29895c635efSGarrett D'Amore {
299260e9a87SYuri Pankov 	while (n != NULL) {
300260e9a87SYuri Pankov 		print_mdoc_node(p, pair, meta, n);
301260e9a87SYuri Pankov 		n = n->next;
302260e9a87SYuri Pankov 	}
30395c635efSGarrett D'Amore }
30495c635efSGarrett D'Amore 
30595c635efSGarrett D'Amore static void
print_mdoc_node(DECL_ARGS)30695c635efSGarrett D'Amore print_mdoc_node(DECL_ARGS)
30795c635efSGarrett D'Amore {
308cec8643bSMichal Nowak 	const struct mdoc_term_act *act;
30995c635efSGarrett D'Amore 	struct termpair	 npair;
31095c635efSGarrett D'Amore 	size_t		 offset, rmargin;
311cec8643bSMichal Nowak 	int		 chld;
312cec8643bSMichal Nowak 
313cec8643bSMichal Nowak 	/*
314cec8643bSMichal Nowak 	 * In no-fill mode, break the output line at the beginning
315cec8643bSMichal Nowak 	 * of new input lines except after \c, and nowhere else.
316cec8643bSMichal Nowak 	 */
317cec8643bSMichal Nowak 
318cec8643bSMichal Nowak 	if (n->flags & NODE_NOFILL) {
319cec8643bSMichal Nowak 		if (n->flags & NODE_LINE &&
320cec8643bSMichal Nowak 		    (p->flags & TERMP_NONEWLINE) == 0)
321cec8643bSMichal Nowak 			term_newln(p);
322cec8643bSMichal Nowak 		p->flags |= TERMP_BRNEVER;
323cec8643bSMichal Nowak 	} else
324cec8643bSMichal Nowak 		p->flags &= ~TERMP_BRNEVER;
32595c635efSGarrett D'Amore 
3266640c13bSYuri Pankov 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
327a40ea1a7SYuri Pankov 		return;
328a40ea1a7SYuri Pankov 
32995c635efSGarrett D'Amore 	chld = 1;
330c66b8046SYuri Pankov 	offset = p->tcol->offset;
331c66b8046SYuri Pankov 	rmargin = p->tcol->rmargin;
332a40ea1a7SYuri Pankov 	n->flags &= ~NODE_ENDED;
333260e9a87SYuri Pankov 	n->prev_font = p->fonti;
33495c635efSGarrett D'Amore 
33595c635efSGarrett D'Amore 	memset(&npair, 0, sizeof(struct termpair));
33695c635efSGarrett D'Amore 	npair.ppair = pair;
33795c635efSGarrett D'Amore 
338*4d131170SRobert Mustacchi 	if (n->flags & NODE_ID && n->tok != MDOC_Pp &&
339*4d131170SRobert Mustacchi 	    (n->tok != MDOC_It || n->type != ROFFT_BLOCK))
340*4d131170SRobert Mustacchi 		term_tag_write(n, p->line);
341*4d131170SRobert Mustacchi 
34295c635efSGarrett D'Amore 	/*
34395c635efSGarrett D'Amore 	 * Keeps only work until the end of a line.  If a keep was
34495c635efSGarrett D'Amore 	 * invoked in a prior line, revert it to PREKEEP.
34595c635efSGarrett D'Amore 	 */
34695c635efSGarrett D'Amore 
347a40ea1a7SYuri Pankov 	if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
348260e9a87SYuri Pankov 		p->flags &= ~TERMP_KEEP;
349260e9a87SYuri Pankov 		p->flags |= TERMP_PREKEEP;
35095c635efSGarrett D'Amore 	}
35195c635efSGarrett D'Amore 
35295c635efSGarrett D'Amore 	/*
35395c635efSGarrett D'Amore 	 * After the keep flags have been set up, we may now
35495c635efSGarrett D'Amore 	 * produce output.  Note that some pre-handlers do so.
35595c635efSGarrett D'Amore 	 */
35695c635efSGarrett D'Amore 
357cec8643bSMichal Nowak 	act = NULL;
35895c635efSGarrett D'Amore 	switch (n->type) {
359371584c2SYuri Pankov 	case ROFFT_TEXT:
360cec8643bSMichal Nowak 		if (n->flags & NODE_LINE) {
361cec8643bSMichal Nowak 			switch (*n->string) {
362cec8643bSMichal Nowak 			case '\0':
363cec8643bSMichal Nowak 				if (p->flags & TERMP_NONEWLINE)
364cec8643bSMichal Nowak 					term_newln(p);
365cec8643bSMichal Nowak 				else
366cec8643bSMichal Nowak 					term_vspace(p);
367cec8643bSMichal Nowak 				return;
368cec8643bSMichal Nowak 			case ' ':
369cec8643bSMichal Nowak 				if ((p->flags & TERMP_NONEWLINE) == 0)
370cec8643bSMichal Nowak 					term_newln(p);
371cec8643bSMichal Nowak 				break;
372cec8643bSMichal Nowak 			default:
373cec8643bSMichal Nowak 				break;
374cec8643bSMichal Nowak 			}
375cec8643bSMichal Nowak 		}
376a40ea1a7SYuri Pankov 		if (NODE_DELIMC & n->flags)
37795c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
37895c635efSGarrett D'Amore 		term_word(p, n->string);
379a40ea1a7SYuri Pankov 		if (NODE_DELIMO & n->flags)
38095c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
38195c635efSGarrett D'Amore 		break;
382371584c2SYuri Pankov 	case ROFFT_EQN:
383a40ea1a7SYuri Pankov 		if ( ! (n->flags & NODE_LINE))
384260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
38595c635efSGarrett D'Amore 		term_eqn(p, n->eqn);
386a40ea1a7SYuri Pankov 		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
387260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
38895c635efSGarrett D'Amore 		break;
389371584c2SYuri Pankov 	case ROFFT_TBL:
390260e9a87SYuri Pankov 		if (p->tbl.cols == NULL)
391260e9a87SYuri Pankov 			term_newln(p);
39295c635efSGarrett D'Amore 		term_tbl(p, n->span);
39395c635efSGarrett D'Amore 		break;
39495c635efSGarrett D'Amore 	default:
395c66b8046SYuri Pankov 		if (n->tok < ROFF_MAX) {
396c66b8046SYuri Pankov 			roff_term_pre(p, n);
397c66b8046SYuri Pankov 			return;
398c66b8046SYuri Pankov 		}
399c66b8046SYuri Pankov 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
400cec8643bSMichal Nowak 		act = mdoc_term_acts + (n->tok - MDOC_Dd);
401cec8643bSMichal Nowak 		if (act->pre != NULL &&
402371584c2SYuri Pankov 		    (n->end == ENDBODY_NOT || n->child != NULL))
403cec8643bSMichal Nowak 			chld = (*act->pre)(p, &npair, meta, n);
40495c635efSGarrett D'Amore 		break;
40595c635efSGarrett D'Amore 	}
40695c635efSGarrett D'Amore 
40795c635efSGarrett D'Amore 	if (chld && n->child)
408698f87a4SGarrett D'Amore 		print_mdoc_nodelist(p, &npair, meta, n->child);
40995c635efSGarrett D'Amore 
410698f87a4SGarrett D'Amore 	term_fontpopq(p,
411260e9a87SYuri Pankov 	    (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
41295c635efSGarrett D'Amore 
41395c635efSGarrett D'Amore 	switch (n->type) {
414371584c2SYuri Pankov 	case ROFFT_TEXT:
41595c635efSGarrett D'Amore 		break;
416371584c2SYuri Pankov 	case ROFFT_TBL:
41795c635efSGarrett D'Amore 		break;
418371584c2SYuri Pankov 	case ROFFT_EQN:
41995c635efSGarrett D'Amore 		break;
42095c635efSGarrett D'Amore 	default:
421cec8643bSMichal Nowak 		if (act->post == NULL || n->flags & NODE_ENDED)
42295c635efSGarrett D'Amore 			break;
423cec8643bSMichal Nowak 		(void)(*act->post)(p, &npair, meta, n);
42495c635efSGarrett D'Amore 
42595c635efSGarrett D'Amore 		/*
42695c635efSGarrett D'Amore 		 * Explicit end tokens not only call the post
42795c635efSGarrett D'Amore 		 * handler, but also tell the respective block
42895c635efSGarrett D'Amore 		 * that it must not call the post handler again.
42995c635efSGarrett D'Amore 		 */
43095c635efSGarrett D'Amore 		if (ENDBODY_NOT != n->end)
431a40ea1a7SYuri Pankov 			n->body->flags |= NODE_ENDED;
43295c635efSGarrett D'Amore 		break;
43395c635efSGarrett D'Amore 	}
43495c635efSGarrett D'Amore 
435a40ea1a7SYuri Pankov 	if (NODE_EOS & n->flags)
43695c635efSGarrett D'Amore 		p->flags |= TERMP_SENTENCE;
43795c635efSGarrett D'Amore 
438c66b8046SYuri Pankov 	if (n->type != ROFFT_TEXT)
439c66b8046SYuri Pankov 		p->tcol->offset = offset;
440c66b8046SYuri Pankov 	p->tcol->rmargin = rmargin;
44195c635efSGarrett D'Amore }
44295c635efSGarrett D'Amore 
44395c635efSGarrett D'Amore static void
print_mdoc_foot(struct termp * p,const struct roff_meta * meta)444371584c2SYuri Pankov print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
44595c635efSGarrett D'Amore {
446260e9a87SYuri Pankov 	size_t sz;
44795c635efSGarrett D'Amore 
44895c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_NONE);
44995c635efSGarrett D'Amore 
450260e9a87SYuri Pankov 	/*
45195c635efSGarrett D'Amore 	 * Output the footer in new-groff style, that is, three columns
45295c635efSGarrett D'Amore 	 * with the middle being the manual date and flanking columns
45395c635efSGarrett D'Amore 	 * being the operating system:
45495c635efSGarrett D'Amore 	 *
45595c635efSGarrett D'Amore 	 * SYSTEM                  DATE                    SYSTEM
45695c635efSGarrett D'Amore 	 */
45795c635efSGarrett D'Amore 
45895c635efSGarrett D'Amore 	term_vspace(p);
45995c635efSGarrett D'Amore 
460c66b8046SYuri Pankov 	p->tcol->offset = 0;
461260e9a87SYuri Pankov 	sz = term_strlen(p, meta->date);
462c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin > sz ?
463260e9a87SYuri Pankov 	    (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
464698f87a4SGarrett D'Amore 	p->trailspace = 1;
46595c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
46695c635efSGarrett D'Amore 
467698f87a4SGarrett D'Amore 	term_word(p, meta->os);
46895c635efSGarrett D'Amore 	term_flushln(p);
46995c635efSGarrett D'Amore 
470c66b8046SYuri Pankov 	p->tcol->offset = p->tcol->rmargin;
471260e9a87SYuri Pankov 	sz = term_strlen(p, meta->os);
472c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
47395c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
47495c635efSGarrett D'Amore 
475698f87a4SGarrett D'Amore 	term_word(p, meta->date);
47695c635efSGarrett D'Amore 	term_flushln(p);
47795c635efSGarrett D'Amore 
478c66b8046SYuri Pankov 	p->tcol->offset = p->tcol->rmargin;
479c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin;
480698f87a4SGarrett D'Amore 	p->trailspace = 0;
48195c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
48295c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
48395c635efSGarrett D'Amore 
484698f87a4SGarrett D'Amore 	term_word(p, meta->os);
48595c635efSGarrett D'Amore 	term_flushln(p);
48695c635efSGarrett D'Amore 
487c66b8046SYuri Pankov 	p->tcol->offset = 0;
488c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin;
48995c635efSGarrett D'Amore 	p->flags = 0;
49095c635efSGarrett D'Amore }
49195c635efSGarrett D'Amore 
49295c635efSGarrett D'Amore static void
print_mdoc_head(struct termp * p,const struct roff_meta * meta)493371584c2SYuri Pankov print_mdoc_head(struct termp *p, const struct roff_meta *meta)
49495c635efSGarrett D'Amore {
495260e9a87SYuri Pankov 	char			*volume, *title;
496260e9a87SYuri Pankov 	size_t			 vollen, titlen;
49795c635efSGarrett D'Amore 
49895c635efSGarrett D'Amore 	/*
49995c635efSGarrett D'Amore 	 * The header is strange.  It has three components, which are
50095c635efSGarrett D'Amore 	 * really two with the first duplicated.  It goes like this:
50195c635efSGarrett D'Amore 	 *
50295c635efSGarrett D'Amore 	 * IDENTIFIER              TITLE                   IDENTIFIER
50395c635efSGarrett D'Amore 	 *
50495c635efSGarrett D'Amore 	 * The IDENTIFIER is NAME(SECTION), which is the command-name
50595c635efSGarrett D'Amore 	 * (if given, or "unknown" if not) followed by the manual page
50695c635efSGarrett D'Amore 	 * section.  These are given in `Dt'.  The TITLE is a free-form
50795c635efSGarrett D'Amore 	 * string depending on the manual volume.  If not specified, it
50895c635efSGarrett D'Amore 	 * switches on the manual section.
50995c635efSGarrett D'Amore 	 */
51095c635efSGarrett D'Amore 
511698f87a4SGarrett D'Amore 	assert(meta->vol);
512260e9a87SYuri Pankov 	if (NULL == meta->arch)
513260e9a87SYuri Pankov 		volume = mandoc_strdup(meta->vol);
514260e9a87SYuri Pankov 	else
515260e9a87SYuri Pankov 		mandoc_asprintf(&volume, "%s (%s)",
516260e9a87SYuri Pankov 		    meta->vol, meta->arch);
517260e9a87SYuri Pankov 	vollen = term_strlen(p, volume);
51895c635efSGarrett D'Amore 
519260e9a87SYuri Pankov 	if (NULL == meta->msec)
520260e9a87SYuri Pankov 		title = mandoc_strdup(meta->title);
521260e9a87SYuri Pankov 	else
522260e9a87SYuri Pankov 		mandoc_asprintf(&title, "%s(%s)",
523260e9a87SYuri Pankov 		    meta->title, meta->msec);
52495c635efSGarrett D'Amore 	titlen = term_strlen(p, title);
52595c635efSGarrett D'Amore 
52695c635efSGarrett D'Amore 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
527698f87a4SGarrett D'Amore 	p->trailspace = 1;
528c66b8046SYuri Pankov 	p->tcol->offset = 0;
529c66b8046SYuri Pankov 	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
530260e9a87SYuri Pankov 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
531260e9a87SYuri Pankov 	    vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
53295c635efSGarrett D'Amore 
53395c635efSGarrett D'Amore 	term_word(p, title);
53495c635efSGarrett D'Amore 	term_flushln(p);
53595c635efSGarrett D'Amore 
53695c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
537c66b8046SYuri Pankov 	p->tcol->offset = p->tcol->rmargin;
538c66b8046SYuri Pankov 	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
539c66b8046SYuri Pankov 	    p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
54095c635efSGarrett D'Amore 
541260e9a87SYuri Pankov 	term_word(p, volume);
54295c635efSGarrett D'Amore 	term_flushln(p);
54395c635efSGarrett D'Amore 
54495c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
545698f87a4SGarrett D'Amore 	p->trailspace = 0;
546c66b8046SYuri Pankov 	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
54795c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
548c66b8046SYuri Pankov 		p->tcol->offset = p->tcol->rmargin;
549c66b8046SYuri Pankov 		p->tcol->rmargin = p->maxrmargin;
55095c635efSGarrett D'Amore 		term_word(p, title);
55195c635efSGarrett D'Amore 		term_flushln(p);
55295c635efSGarrett D'Amore 	}
55395c635efSGarrett D'Amore 
55495c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOSPACE;
555c66b8046SYuri Pankov 	p->tcol->offset = 0;
556c66b8046SYuri Pankov 	p->tcol->rmargin = p->maxrmargin;
557260e9a87SYuri Pankov 	free(title);
558260e9a87SYuri Pankov 	free(volume);
55995c635efSGarrett D'Amore }
56095c635efSGarrett D'Amore 
561260e9a87SYuri Pankov static int
a2width(const struct termp * p,const char * v)56295c635efSGarrett D'Amore a2width(const struct termp *p, const char *v)
56395c635efSGarrett D'Amore {
56495c635efSGarrett D'Amore 	struct roffsu	 su;
565c66b8046SYuri Pankov 	const char	*end;
56695c635efSGarrett D'Amore 
567c66b8046SYuri Pankov 	end = a2roffsu(v, &su, SCALE_MAX);
568c66b8046SYuri Pankov 	if (end == NULL || *end != '\0') {
56995c635efSGarrett D'Amore 		SCALE_HS_INIT(&su, term_strlen(p, v));
570260e9a87SYuri Pankov 		su.scale /= term_strlen(p, "0");
571260e9a87SYuri Pankov 	}
572c66b8046SYuri Pankov 	return term_hen(p, &su);
57395c635efSGarrett D'Amore }
57495c635efSGarrett D'Amore 
57595c635efSGarrett D'Amore /*
57695c635efSGarrett D'Amore  * Determine how much space to print out before block elements of `It'
57795c635efSGarrett D'Amore  * (and thus `Bl') and `Bd'.  And then go ahead and print that space,
57895c635efSGarrett D'Amore  * too.
57995c635efSGarrett D'Amore  */
58095c635efSGarrett D'Amore static void
print_bvspace(struct termp * p,struct roff_node * bl,struct roff_node * n)581*4d131170SRobert Mustacchi print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n)
58295c635efSGarrett D'Amore {
583*4d131170SRobert Mustacchi 	struct roff_node *nn;
58495c635efSGarrett D'Amore 
58595c635efSGarrett D'Amore 	term_newln(p);
58695c635efSGarrett D'Amore 
587*4d131170SRobert Mustacchi 	if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) ||
588*4d131170SRobert Mustacchi 	    (bl->tok == MDOC_Bl && bl->norm->Bl.comp))
58995c635efSGarrett D'Amore 		return;
59095c635efSGarrett D'Amore 
59195c635efSGarrett D'Amore 	/* Do not vspace directly after Ss/Sh. */
59295c635efSGarrett D'Amore 
593260e9a87SYuri Pankov 	nn = n;
594*4d131170SRobert Mustacchi 	while (roff_node_prev(nn) == NULL) {
595260e9a87SYuri Pankov 		do {
596260e9a87SYuri Pankov 			nn = nn->parent;
597371584c2SYuri Pankov 			if (nn->type == ROFFT_ROOT)
598260e9a87SYuri Pankov 				return;
599371584c2SYuri Pankov 		} while (nn->type != ROFFT_BLOCK);
600260e9a87SYuri Pankov 		if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
60195c635efSGarrett D'Amore 			return;
602260e9a87SYuri Pankov 		if (nn->tok == MDOC_It &&
603260e9a87SYuri Pankov 		    nn->parent->parent->norm->Bl.type != LIST_item)
604260e9a87SYuri Pankov 			break;
60595c635efSGarrett D'Amore 	}
60695c635efSGarrett D'Amore 
607*4d131170SRobert Mustacchi 	/*
608*4d131170SRobert Mustacchi 	 * No vertical space after:
609*4d131170SRobert Mustacchi 	 * items in .Bl -column
610*4d131170SRobert Mustacchi 	 * items without a body in .Bl -diag
611*4d131170SRobert Mustacchi 	 */
61295c635efSGarrett D'Amore 
613*4d131170SRobert Mustacchi 	if (bl->tok != MDOC_Bl ||
614*4d131170SRobert Mustacchi 	    n->prev == NULL || n->prev->tok != MDOC_It ||
615*4d131170SRobert Mustacchi 	    (bl->norm->Bl.type != LIST_column &&
616*4d131170SRobert Mustacchi 	     (bl->norm->Bl.type != LIST_diag ||
617*4d131170SRobert Mustacchi 	      n->prev->body->child != NULL)))
618*4d131170SRobert Mustacchi 		term_vspace(p);
61995c635efSGarrett D'Amore }
62095c635efSGarrett D'Amore 
62195c635efSGarrett D'Amore 
62295c635efSGarrett D'Amore static int
termp_it_pre(DECL_ARGS)62395c635efSGarrett D'Amore termp_it_pre(DECL_ARGS)
62495c635efSGarrett D'Amore {
625a40ea1a7SYuri Pankov 	struct roffsu		su;
626260e9a87SYuri Pankov 	char			buf[24];
627371584c2SYuri Pankov 	const struct roff_node *bl, *nn;
628260e9a87SYuri Pankov 	size_t			ncols, dcol;
629260e9a87SYuri Pankov 	int			i, offset, width;
63095c635efSGarrett D'Amore 	enum mdoc_list		type;
63195c635efSGarrett D'Amore 
632371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK) {
63395c635efSGarrett D'Amore 		print_bvspace(p, n->parent->parent, n);
634*4d131170SRobert Mustacchi 		if (n->flags & NODE_ID)
635*4d131170SRobert Mustacchi 			term_tag_write(n, p->line);
636371584c2SYuri Pankov 		return 1;
63795c635efSGarrett D'Amore 	}
63895c635efSGarrett D'Amore 
63995c635efSGarrett D'Amore 	bl = n->parent->parent->parent;
64095c635efSGarrett D'Amore 	type = bl->norm->Bl.type;
64195c635efSGarrett D'Amore 
642260e9a87SYuri Pankov 	/*
643260e9a87SYuri Pankov 	 * Defaults for specific list types.
644260e9a87SYuri Pankov 	 */
645260e9a87SYuri Pankov 
646260e9a87SYuri Pankov 	switch (type) {
647260e9a87SYuri Pankov 	case LIST_bullet:
648260e9a87SYuri Pankov 	case LIST_dash:
649260e9a87SYuri Pankov 	case LIST_hyphen:
650260e9a87SYuri Pankov 	case LIST_enum:
651260e9a87SYuri Pankov 		width = term_len(p, 2);
652260e9a87SYuri Pankov 		break;
653260e9a87SYuri Pankov 	case LIST_hang:
654a40ea1a7SYuri Pankov 	case LIST_tag:
655260e9a87SYuri Pankov 		width = term_len(p, 8);
656260e9a87SYuri Pankov 		break;
657260e9a87SYuri Pankov 	case LIST_column:
658260e9a87SYuri Pankov 		width = term_len(p, 10);
659260e9a87SYuri Pankov 		break;
660260e9a87SYuri Pankov 	default:
661260e9a87SYuri Pankov 		width = 0;
662260e9a87SYuri Pankov 		break;
663260e9a87SYuri Pankov 	}
664260e9a87SYuri Pankov 	offset = 0;
665260e9a87SYuri Pankov 
666260e9a87SYuri Pankov 	/*
66795c635efSGarrett D'Amore 	 * First calculate width and offset.  This is pretty easy unless
66895c635efSGarrett D'Amore 	 * we're a -column list, in which case all prior columns must
66995c635efSGarrett D'Amore 	 * be accounted for.
67095c635efSGarrett D'Amore 	 */
67195c635efSGarrett D'Amore 
672260e9a87SYuri Pankov 	if (bl->norm->Bl.offs != NULL) {
673260e9a87SYuri Pankov 		offset = a2width(p, bl->norm->Bl.offs);
674c66b8046SYuri Pankov 		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
675c66b8046SYuri Pankov 			offset = -p->tcol->offset;
676260e9a87SYuri Pankov 		else if (offset > SHRT_MAX)
677260e9a87SYuri Pankov 			offset = 0;
678260e9a87SYuri Pankov 	}
67995c635efSGarrett D'Amore 
68095c635efSGarrett D'Amore 	switch (type) {
681260e9a87SYuri Pankov 	case LIST_column:
682371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
68395c635efSGarrett D'Amore 			break;
68495c635efSGarrett D'Amore 
68595c635efSGarrett D'Amore 		/*
68695c635efSGarrett D'Amore 		 * Imitate groff's column handling:
68795c635efSGarrett D'Amore 		 * - For each earlier column, add its width.
68895c635efSGarrett D'Amore 		 * - For less than 5 columns, add four more blanks per
68995c635efSGarrett D'Amore 		 *   column.
69095c635efSGarrett D'Amore 		 * - For exactly 5 columns, add three more blank per
69195c635efSGarrett D'Amore 		 *   column.
69295c635efSGarrett D'Amore 		 * - For more than 5 columns, add only one column.
69395c635efSGarrett D'Amore 		 */
69495c635efSGarrett D'Amore 		ncols = bl->norm->Bl.ncols;
695260e9a87SYuri Pankov 		dcol = ncols < 5 ? term_len(p, 4) :
696260e9a87SYuri Pankov 		    ncols == 5 ? term_len(p, 3) : term_len(p, 1);
69795c635efSGarrett D'Amore 
69895c635efSGarrett D'Amore 		/*
699371584c2SYuri Pankov 		 * Calculate the offset by applying all prior ROFFT_BODY,
700371584c2SYuri Pankov 		 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
70195c635efSGarrett D'Amore 		 */
70295c635efSGarrett D'Amore 
703260e9a87SYuri Pankov 		for (i = 0, nn = n->prev;
704260e9a87SYuri Pankov 		    nn->prev && i < (int)ncols;
705a40ea1a7SYuri Pankov 		    nn = nn->prev, i++) {
706a40ea1a7SYuri Pankov 			SCALE_HS_INIT(&su,
707a40ea1a7SYuri Pankov 			    term_strlen(p, bl->norm->Bl.cols[i]));
708a40ea1a7SYuri Pankov 			su.scale /= term_strlen(p, "0");
709c66b8046SYuri Pankov 			offset += term_hen(p, &su) + dcol;
710a40ea1a7SYuri Pankov 		}
71195c635efSGarrett D'Amore 
71295c635efSGarrett D'Amore 		/*
71395c635efSGarrett D'Amore 		 * When exceeding the declared number of columns, leave
71495c635efSGarrett D'Amore 		 * the remaining widths at 0.  This will later be
71595c635efSGarrett D'Amore 		 * adjusted to the default width of 10, or, for the last
71695c635efSGarrett D'Amore 		 * column, stretched to the right margin.
71795c635efSGarrett D'Amore 		 */
71895c635efSGarrett D'Amore 		if (i >= (int)ncols)
71995c635efSGarrett D'Amore 			break;
72095c635efSGarrett D'Amore 
72195c635efSGarrett D'Amore 		/*
72295c635efSGarrett D'Amore 		 * Use the declared column widths, extended as explained
72395c635efSGarrett D'Amore 		 * in the preceding paragraph.
72495c635efSGarrett D'Amore 		 */
725a40ea1a7SYuri Pankov 		SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
726a40ea1a7SYuri Pankov 		su.scale /= term_strlen(p, "0");
727c66b8046SYuri Pankov 		width = term_hen(p, &su) + dcol;
72895c635efSGarrett D'Amore 		break;
72995c635efSGarrett D'Amore 	default:
73095c635efSGarrett D'Amore 		if (NULL == bl->norm->Bl.width)
73195c635efSGarrett D'Amore 			break;
73295c635efSGarrett D'Amore 
733260e9a87SYuri Pankov 		/*
73495c635efSGarrett D'Amore 		 * Note: buffer the width by 2, which is groff's magic
73595c635efSGarrett D'Amore 		 * number for buffering single arguments.  See the above
73695c635efSGarrett D'Amore 		 * handling for column for how this changes.
73795c635efSGarrett D'Amore 		 */
73895c635efSGarrett D'Amore 		width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
739c66b8046SYuri Pankov 		if (width < 0 && (size_t)(-width) > p->tcol->offset)
740c66b8046SYuri Pankov 			width = -p->tcol->offset;
741260e9a87SYuri Pankov 		else if (width > SHRT_MAX)
742260e9a87SYuri Pankov 			width = 0;
74395c635efSGarrett D'Amore 		break;
74495c635efSGarrett D'Amore 	}
74595c635efSGarrett D'Amore 
746260e9a87SYuri Pankov 	/*
74795c635efSGarrett D'Amore 	 * Whitespace control.  Inset bodies need an initial space,
74895c635efSGarrett D'Amore 	 * while diagonal bodies need two.
74995c635efSGarrett D'Amore 	 */
75095c635efSGarrett D'Amore 
75195c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
75295c635efSGarrett D'Amore 
75395c635efSGarrett D'Amore 	switch (type) {
754260e9a87SYuri Pankov 	case LIST_diag:
755371584c2SYuri Pankov 		if (n->type == ROFFT_BODY)
75695c635efSGarrett D'Amore 			term_word(p, "\\ \\ ");
75795c635efSGarrett D'Amore 		break;
758260e9a87SYuri Pankov 	case LIST_inset:
759371584c2SYuri Pankov 		if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
76095c635efSGarrett D'Amore 			term_word(p, "\\ ");
76195c635efSGarrett D'Amore 		break;
76295c635efSGarrett D'Amore 	default:
76395c635efSGarrett D'Amore 		break;
76495c635efSGarrett D'Amore 	}
76595c635efSGarrett D'Amore 
76695c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
76795c635efSGarrett D'Amore 
76895c635efSGarrett D'Amore 	switch (type) {
769260e9a87SYuri Pankov 	case LIST_diag:
770371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
77195c635efSGarrett D'Amore 			term_fontpush(p, TERMFONT_BOLD);
77295c635efSGarrett D'Amore 		break;
77395c635efSGarrett D'Amore 	default:
77495c635efSGarrett D'Amore 		break;
77595c635efSGarrett D'Amore 	}
77695c635efSGarrett D'Amore 
77795c635efSGarrett D'Amore 	/*
77895c635efSGarrett D'Amore 	 * Pad and break control.  This is the tricky part.  These flags
77995c635efSGarrett D'Amore 	 * are documented in term_flushln() in term.c.  Note that we're
78095c635efSGarrett D'Amore 	 * going to unset all of these flags in termp_it_post() when we
78195c635efSGarrett D'Amore 	 * exit.
78295c635efSGarrett D'Amore 	 */
78395c635efSGarrett D'Amore 
78495c635efSGarrett D'Amore 	switch (type) {
785260e9a87SYuri Pankov 	case LIST_enum:
786371584c2SYuri Pankov 	case LIST_bullet:
787371584c2SYuri Pankov 	case LIST_dash:
788371584c2SYuri Pankov 	case LIST_hyphen:
789c66b8046SYuri Pankov 		if (n->type == ROFFT_HEAD) {
790c66b8046SYuri Pankov 			p->flags |= TERMP_NOBREAK | TERMP_HANG;
791c66b8046SYuri Pankov 			p->trailspace = 1;
792c66b8046SYuri Pankov 		} else if (width <= (int)term_len(p, 2))
793c66b8046SYuri Pankov 			p->flags |= TERMP_NOPAD;
79495c635efSGarrett D'Amore 		break;
795260e9a87SYuri Pankov 	case LIST_hang:
796371584c2SYuri Pankov 		if (n->type != ROFFT_HEAD)
79795c635efSGarrett D'Amore 			break;
798260e9a87SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
799698f87a4SGarrett D'Amore 		p->trailspace = 1;
80095c635efSGarrett D'Amore 		break;
801260e9a87SYuri Pankov 	case LIST_tag:
802371584c2SYuri Pankov 		if (n->type != ROFFT_HEAD)
80395c635efSGarrett D'Amore 			break;
804698f87a4SGarrett D'Amore 
805371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
806698f87a4SGarrett D'Amore 		p->trailspace = 2;
807698f87a4SGarrett D'Amore 
80895c635efSGarrett D'Amore 		if (NULL == n->next || NULL == n->next->child)
809c66b8046SYuri Pankov 			p->flags |= TERMP_HANG;
81095c635efSGarrett D'Amore 		break;
811260e9a87SYuri Pankov 	case LIST_column:
812371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
81395c635efSGarrett D'Amore 			break;
81495c635efSGarrett D'Amore 
815698f87a4SGarrett D'Amore 		if (NULL == n->next) {
81695c635efSGarrett D'Amore 			p->flags &= ~TERMP_NOBREAK;
817698f87a4SGarrett D'Amore 			p->trailspace = 0;
818698f87a4SGarrett D'Amore 		} else {
81995c635efSGarrett D'Amore 			p->flags |= TERMP_NOBREAK;
820698f87a4SGarrett D'Amore 			p->trailspace = 1;
821698f87a4SGarrett D'Amore 		}
82295c635efSGarrett D'Amore 
82395c635efSGarrett D'Amore 		break;
824260e9a87SYuri Pankov 	case LIST_diag:
825371584c2SYuri Pankov 		if (n->type != ROFFT_HEAD)
826698f87a4SGarrett D'Amore 			break;
827260e9a87SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
828698f87a4SGarrett D'Amore 		p->trailspace = 1;
82995c635efSGarrett D'Amore 		break;
83095c635efSGarrett D'Amore 	default:
83195c635efSGarrett D'Amore 		break;
83295c635efSGarrett D'Amore 	}
83395c635efSGarrett D'Amore 
834260e9a87SYuri Pankov 	/*
83595c635efSGarrett D'Amore 	 * Margin control.  Set-head-width lists have their right
83695c635efSGarrett D'Amore 	 * margins shortened.  The body for these lists has the offset
83795c635efSGarrett D'Amore 	 * necessarily lengthened.  Everybody gets the offset.
83895c635efSGarrett D'Amore 	 */
83995c635efSGarrett D'Amore 
840c66b8046SYuri Pankov 	p->tcol->offset += offset;
84195c635efSGarrett D'Amore 
84295c635efSGarrett D'Amore 	switch (type) {
843260e9a87SYuri Pankov 	case LIST_bullet:
844260e9a87SYuri Pankov 	case LIST_dash:
845260e9a87SYuri Pankov 	case LIST_enum:
846260e9a87SYuri Pankov 	case LIST_hyphen:
847c66b8046SYuri Pankov 	case LIST_hang:
848260e9a87SYuri Pankov 	case LIST_tag:
849371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
850c66b8046SYuri Pankov 			p->tcol->rmargin = p->tcol->offset + width;
851260e9a87SYuri Pankov 		else
852c66b8046SYuri Pankov 			p->tcol->offset += width;
85395c635efSGarrett D'Amore 		break;
854260e9a87SYuri Pankov 	case LIST_column:
85595c635efSGarrett D'Amore 		assert(width);
856c66b8046SYuri Pankov 		p->tcol->rmargin = p->tcol->offset + width;
857260e9a87SYuri Pankov 		/*
85895c635efSGarrett D'Amore 		 * XXX - this behaviour is not documented: the
85995c635efSGarrett D'Amore 		 * right-most column is filled to the right margin.
86095c635efSGarrett D'Amore 		 */
861371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
86295c635efSGarrett D'Amore 			break;
863c66b8046SYuri Pankov 		if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
864c66b8046SYuri Pankov 			p->tcol->rmargin = p->maxrmargin;
86595c635efSGarrett D'Amore 		break;
86695c635efSGarrett D'Amore 	default:
86795c635efSGarrett D'Amore 		break;
86895c635efSGarrett D'Amore 	}
86995c635efSGarrett D'Amore 
870260e9a87SYuri Pankov 	/*
87195c635efSGarrett D'Amore 	 * The dash, hyphen, bullet and enum lists all have a special
872260e9a87SYuri Pankov 	 * HEAD character (temporarily bold, in some cases).
87395c635efSGarrett D'Amore 	 */
87495c635efSGarrett D'Amore 
875371584c2SYuri Pankov 	if (n->type == ROFFT_HEAD)
87695c635efSGarrett D'Amore 		switch (type) {
877260e9a87SYuri Pankov 		case LIST_bullet:
87895c635efSGarrett D'Amore 			term_fontpush(p, TERMFONT_BOLD);
87995c635efSGarrett D'Amore 			term_word(p, "\\[bu]");
88095c635efSGarrett D'Amore 			term_fontpop(p);
88195c635efSGarrett D'Amore 			break;
882260e9a87SYuri Pankov 		case LIST_dash:
883260e9a87SYuri Pankov 		case LIST_hyphen:
88495c635efSGarrett D'Amore 			term_fontpush(p, TERMFONT_BOLD);
885371584c2SYuri Pankov 			term_word(p, "-");
88695c635efSGarrett D'Amore 			term_fontpop(p);
88795c635efSGarrett D'Amore 			break;
888260e9a87SYuri Pankov 		case LIST_enum:
88995c635efSGarrett D'Amore 			(pair->ppair->ppair->count)++;
890260e9a87SYuri Pankov 			(void)snprintf(buf, sizeof(buf), "%d.",
891260e9a87SYuri Pankov 			    pair->ppair->ppair->count);
89295c635efSGarrett D'Amore 			term_word(p, buf);
89395c635efSGarrett D'Amore 			break;
89495c635efSGarrett D'Amore 		default:
89595c635efSGarrett D'Amore 			break;
89695c635efSGarrett D'Amore 		}
89795c635efSGarrett D'Amore 
898260e9a87SYuri Pankov 	/*
89995c635efSGarrett D'Amore 	 * If we're not going to process our children, indicate so here.
90095c635efSGarrett D'Amore 	 */
90195c635efSGarrett D'Amore 
90295c635efSGarrett D'Amore 	switch (type) {
903260e9a87SYuri Pankov 	case LIST_bullet:
904260e9a87SYuri Pankov 	case LIST_item:
905260e9a87SYuri Pankov 	case LIST_dash:
906260e9a87SYuri Pankov 	case LIST_hyphen:
907260e9a87SYuri Pankov 	case LIST_enum:
908371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
909371584c2SYuri Pankov 			return 0;
91095c635efSGarrett D'Amore 		break;
911260e9a87SYuri Pankov 	case LIST_column:
912371584c2SYuri Pankov 		if (n->type == ROFFT_HEAD)
913371584c2SYuri Pankov 			return 0;
914c66b8046SYuri Pankov 		p->minbl = 0;
91595c635efSGarrett D'Amore 		break;
91695c635efSGarrett D'Amore 	default:
91795c635efSGarrett D'Amore 		break;
91895c635efSGarrett D'Amore 	}
91995c635efSGarrett D'Amore 
920371584c2SYuri Pankov 	return 1;
92195c635efSGarrett D'Amore }
92295c635efSGarrett D'Amore 
92395c635efSGarrett D'Amore static void
termp_it_post(DECL_ARGS)92495c635efSGarrett D'Amore termp_it_post(DECL_ARGS)
92595c635efSGarrett D'Amore {
92695c635efSGarrett D'Amore 	enum mdoc_list	   type;
92795c635efSGarrett D'Amore 
928371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK)
92995c635efSGarrett D'Amore 		return;
93095c635efSGarrett D'Amore 
93195c635efSGarrett D'Amore 	type = n->parent->parent->parent->norm->Bl.type;
93295c635efSGarrett D'Amore 
93395c635efSGarrett D'Amore 	switch (type) {
934260e9a87SYuri Pankov 	case LIST_item:
935260e9a87SYuri Pankov 	case LIST_diag:
936260e9a87SYuri Pankov 	case LIST_inset:
937371584c2SYuri Pankov 		if (n->type == ROFFT_BODY)
93895c635efSGarrett D'Amore 			term_newln(p);
93995c635efSGarrett D'Amore 		break;
940260e9a87SYuri Pankov 	case LIST_column:
941371584c2SYuri Pankov 		if (n->type == ROFFT_BODY)
94295c635efSGarrett D'Amore 			term_flushln(p);
94395c635efSGarrett D'Amore 		break;
94495c635efSGarrett D'Amore 	default:
94595c635efSGarrett D'Amore 		term_newln(p);
94695c635efSGarrett D'Amore 		break;
94795c635efSGarrett D'Amore 	}
94895c635efSGarrett D'Amore 
949260e9a87SYuri Pankov 	/*
95095c635efSGarrett D'Amore 	 * Now that our output is flushed, we can reset our tags.  Since
95195c635efSGarrett D'Amore 	 * only `It' sets these flags, we're free to assume that nobody
95295c635efSGarrett D'Amore 	 * has munged them in the meanwhile.
95395c635efSGarrett D'Amore 	 */
95495c635efSGarrett D'Amore 
955c66b8046SYuri Pankov 	p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
956698f87a4SGarrett D'Amore 	p->trailspace = 0;
95795c635efSGarrett D'Amore }
95895c635efSGarrett D'Amore 
95995c635efSGarrett D'Amore static int
termp_nm_pre(DECL_ARGS)96095c635efSGarrett D'Amore termp_nm_pre(DECL_ARGS)
96195c635efSGarrett D'Amore {
962260e9a87SYuri Pankov 	const char	*cp;
96395c635efSGarrett D'Amore 
964371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK) {
965698f87a4SGarrett D'Amore 		p->flags |= TERMP_PREKEEP;
966371584c2SYuri Pankov 		return 1;
967698f87a4SGarrett D'Amore 	}
96895c635efSGarrett D'Amore 
969371584c2SYuri Pankov 	if (n->type == ROFFT_BODY) {
970c66b8046SYuri Pankov 		if (n->child == NULL)
971371584c2SYuri Pankov 			return 0;
97295c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
973260e9a87SYuri Pankov 		cp = NULL;
974260e9a87SYuri Pankov 		if (n->prev->child != NULL)
975260e9a87SYuri Pankov 		    cp = n->prev->child->string;
976260e9a87SYuri Pankov 		if (cp == NULL)
977260e9a87SYuri Pankov 			cp = meta->name;
978260e9a87SYuri Pankov 		if (cp == NULL)
979c66b8046SYuri Pankov 			p->tcol->offset += term_len(p, 6);
980260e9a87SYuri Pankov 		else
981c66b8046SYuri Pankov 			p->tcol->offset += term_len(p, 1) +
982c66b8046SYuri Pankov 			    term_strlen(p, cp);
983371584c2SYuri Pankov 		return 1;
98495c635efSGarrett D'Amore 	}
98595c635efSGarrett D'Amore 
986a40ea1a7SYuri Pankov 	if (n->child == NULL)
987371584c2SYuri Pankov 		return 0;
98895c635efSGarrett D'Amore 
989371584c2SYuri Pankov 	if (n->type == ROFFT_HEAD)
99095c635efSGarrett D'Amore 		synopsis_pre(p, n->parent);
99195c635efSGarrett D'Amore 
992371584c2SYuri Pankov 	if (n->type == ROFFT_HEAD &&
993c66b8046SYuri Pankov 	    n->next != NULL && n->next->child != NULL) {
994260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
995698f87a4SGarrett D'Amore 		p->trailspace = 1;
996c66b8046SYuri Pankov 		p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
997c66b8046SYuri Pankov 		if (n->child == NULL)
998c66b8046SYuri Pankov 			p->tcol->rmargin += term_strlen(p, meta->name);
999c66b8046SYuri Pankov 		else if (n->child->type == ROFFT_TEXT) {
1000c66b8046SYuri Pankov 			p->tcol->rmargin += term_strlen(p, n->child->string);
1001c66b8046SYuri Pankov 			if (n->child->next != NULL)
100295c635efSGarrett D'Amore 				p->flags |= TERMP_HANG;
100395c635efSGarrett D'Amore 		} else {
1004c66b8046SYuri Pankov 			p->tcol->rmargin += term_len(p, 5);
100595c635efSGarrett D'Amore 			p->flags |= TERMP_HANG;
100695c635efSGarrett D'Amore 		}
100795c635efSGarrett D'Amore 	}
1008*4d131170SRobert Mustacchi 	return termp_bold_pre(p, pair, meta, n);
100995c635efSGarrett D'Amore }
101095c635efSGarrett D'Amore 
101195c635efSGarrett D'Amore static void
termp_nm_post(DECL_ARGS)101295c635efSGarrett D'Amore termp_nm_post(DECL_ARGS)
101395c635efSGarrett D'Amore {
1014*4d131170SRobert Mustacchi 	switch (n->type) {
1015*4d131170SRobert Mustacchi 	case ROFFT_BLOCK:
1016698f87a4SGarrett D'Amore 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1017*4d131170SRobert Mustacchi 		break;
1018*4d131170SRobert Mustacchi 	case ROFFT_HEAD:
1019*4d131170SRobert Mustacchi 		if (n->next == NULL || n->next->child == NULL)
1020*4d131170SRobert Mustacchi 			break;
102195c635efSGarrett D'Amore 		term_flushln(p);
1022260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1023698f87a4SGarrett D'Amore 		p->trailspace = 0;
1024*4d131170SRobert Mustacchi 		break;
1025*4d131170SRobert Mustacchi 	case ROFFT_BODY:
1026*4d131170SRobert Mustacchi 		if (n->child != NULL)
1027*4d131170SRobert Mustacchi 			term_flushln(p);
1028*4d131170SRobert Mustacchi 		break;
1029*4d131170SRobert Mustacchi 	default:
1030*4d131170SRobert Mustacchi 		break;
1031*4d131170SRobert Mustacchi 	}
103295c635efSGarrett D'Amore }
103395c635efSGarrett D'Amore 
103495c635efSGarrett D'Amore static int
termp_fl_pre(DECL_ARGS)103595c635efSGarrett D'Amore termp_fl_pre(DECL_ARGS)
103695c635efSGarrett D'Amore {
1037*4d131170SRobert Mustacchi 	struct roff_node *nn;
103895c635efSGarrett D'Amore 
103995c635efSGarrett D'Amore 	term_fontpush(p, TERMFONT_BOLD);
104095c635efSGarrett D'Amore 	term_word(p, "\\-");
104195c635efSGarrett D'Amore 
1042*4d131170SRobert Mustacchi 	if (n->child != NULL ||
1043*4d131170SRobert Mustacchi 	    ((nn = roff_node_next(n)) != NULL &&
1044*4d131170SRobert Mustacchi 	     nn->type != ROFFT_TEXT &&
1045*4d131170SRobert Mustacchi 	     (nn->flags & NODE_LINE) == 0))
104695c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
104795c635efSGarrett D'Amore 
1048371584c2SYuri Pankov 	return 1;
104995c635efSGarrett D'Amore }
105095c635efSGarrett D'Amore 
105195c635efSGarrett D'Amore static int
termp__a_pre(DECL_ARGS)105295c635efSGarrett D'Amore termp__a_pre(DECL_ARGS)
105395c635efSGarrett D'Amore {
1054*4d131170SRobert Mustacchi 	struct roff_node *nn;
105595c635efSGarrett D'Amore 
1056*4d131170SRobert Mustacchi 	if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A &&
1057*4d131170SRobert Mustacchi 	    ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A))
1058*4d131170SRobert Mustacchi 		term_word(p, "and");
105995c635efSGarrett D'Amore 
1060371584c2SYuri Pankov 	return 1;
106195c635efSGarrett D'Amore }
106295c635efSGarrett D'Amore 
106395c635efSGarrett D'Amore static int
termp_an_pre(DECL_ARGS)106495c635efSGarrett D'Amore termp_an_pre(DECL_ARGS)
106595c635efSGarrett D'Amore {
106695c635efSGarrett D'Amore 
1067260e9a87SYuri Pankov 	if (n->norm->An.auth == AUTH_split) {
106895c635efSGarrett D'Amore 		p->flags &= ~TERMP_NOSPLIT;
106995c635efSGarrett D'Amore 		p->flags |= TERMP_SPLIT;
1070371584c2SYuri Pankov 		return 0;
1071260e9a87SYuri Pankov 	}
1072260e9a87SYuri Pankov 	if (n->norm->An.auth == AUTH_nosplit) {
107395c635efSGarrett D'Amore 		p->flags &= ~TERMP_SPLIT;
107495c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPLIT;
1075371584c2SYuri Pankov 		return 0;
107695c635efSGarrett D'Amore 	}
107795c635efSGarrett D'Amore 
1078260e9a87SYuri Pankov 	if (p->flags & TERMP_SPLIT)
1079260e9a87SYuri Pankov 		term_newln(p);
108095c635efSGarrett D'Amore 
1081260e9a87SYuri Pankov 	if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
1082260e9a87SYuri Pankov 		p->flags |= TERMP_SPLIT;
1083260e9a87SYuri Pankov 
1084371584c2SYuri Pankov 	return 1;
1085260e9a87SYuri Pankov }
108695c635efSGarrett D'Amore 
108795c635efSGarrett D'Amore static int
termp_ns_pre(DECL_ARGS)108895c635efSGarrett D'Amore termp_ns_pre(DECL_ARGS)
108995c635efSGarrett D'Amore {
109095c635efSGarrett D'Amore 
1091a40ea1a7SYuri Pankov 	if ( ! (NODE_LINE & n->flags))
109295c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
1093371584c2SYuri Pankov 	return 1;
109495c635efSGarrett D'Amore }
109595c635efSGarrett D'Amore 
109695c635efSGarrett D'Amore static int
termp_rs_pre(DECL_ARGS)109795c635efSGarrett D'Amore termp_rs_pre(DECL_ARGS)
109895c635efSGarrett D'Amore {
109995c635efSGarrett D'Amore 	if (SEC_SEE_ALSO != n->sec)
1100371584c2SYuri Pankov 		return 1;
1101*4d131170SRobert Mustacchi 	if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL)
110295c635efSGarrett D'Amore 		term_vspace(p);
1103371584c2SYuri Pankov 	return 1;
110495c635efSGarrett D'Amore }
110595c635efSGarrett D'Amore 
110695c635efSGarrett D'Amore static int
termp_ex_pre(DECL_ARGS)110795c635efSGarrett D'Amore termp_ex_pre(DECL_ARGS)
110895c635efSGarrett D'Amore {
110995c635efSGarrett D'Amore 	term_newln(p);
1110a40ea1a7SYuri Pankov 	return 1;
111195c635efSGarrett D'Amore }
111295c635efSGarrett D'Amore 
111395c635efSGarrett D'Amore static int
termp_nd_pre(DECL_ARGS)111495c635efSGarrett D'Amore termp_nd_pre(DECL_ARGS)
111595c635efSGarrett D'Amore {
1116371584c2SYuri Pankov 	if (n->type == ROFFT_BODY)
1117260e9a87SYuri Pankov 		term_word(p, "\\(en");
1118371584c2SYuri Pankov 	return 1;
111995c635efSGarrett D'Amore }
112095c635efSGarrett D'Amore 
112195c635efSGarrett D'Amore static int
termp_bl_pre(DECL_ARGS)112295c635efSGarrett D'Amore termp_bl_pre(DECL_ARGS)
112395c635efSGarrett D'Amore {
1124*4d131170SRobert Mustacchi 	switch (n->type) {
1125*4d131170SRobert Mustacchi 	case ROFFT_BLOCK:
1126*4d131170SRobert Mustacchi 		term_newln(p);
1127*4d131170SRobert Mustacchi 		return 1;
1128*4d131170SRobert Mustacchi 	case ROFFT_HEAD:
1129*4d131170SRobert Mustacchi 		return 0;
1130*4d131170SRobert Mustacchi 	default:
1131*4d131170SRobert Mustacchi 		return 1;
1132*4d131170SRobert Mustacchi 	}
113395c635efSGarrett D'Amore }
113495c635efSGarrett D'Amore 
113595c635efSGarrett D'Amore static void
termp_bl_post(DECL_ARGS)113695c635efSGarrett D'Amore termp_bl_post(DECL_ARGS)
113795c635efSGarrett D'Amore {
1138c66b8046SYuri Pankov 	if (n->type != ROFFT_BLOCK)
1139c66b8046SYuri Pankov 		return;
1140c66b8046SYuri Pankov 	term_newln(p);
1141c66b8046SYuri Pankov 	if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
1142c66b8046SYuri Pankov 		return;
1143c66b8046SYuri Pankov 	term_tab_set(p, NULL);
1144c66b8046SYuri Pankov 	term_tab_set(p, "T");
1145c66b8046SYuri Pankov 	term_tab_set(p, ".5i");
114695c635efSGarrett D'Amore }
114795c635efSGarrett D'Amore 
114895c635efSGarrett D'Amore static int
termp_xr_pre(DECL_ARGS)114995c635efSGarrett D'Amore termp_xr_pre(DECL_ARGS)
115095c635efSGarrett D'Amore {
115195c635efSGarrett D'Amore 	if (NULL == (n = n->child))
1152371584c2SYuri Pankov 		return 0;
115395c635efSGarrett D'Amore 
1154371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
115595c635efSGarrett D'Amore 	term_word(p, n->string);
115695c635efSGarrett D'Amore 
1157260e9a87SYuri Pankov 	if (NULL == (n = n->next))
1158371584c2SYuri Pankov 		return 0;
115995c635efSGarrett D'Amore 
116095c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
116195c635efSGarrett D'Amore 	term_word(p, "(");
116295c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
116395c635efSGarrett D'Amore 
1164371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
116595c635efSGarrett D'Amore 	term_word(p, n->string);
116695c635efSGarrett D'Amore 
116795c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
116895c635efSGarrett D'Amore 	term_word(p, ")");
116995c635efSGarrett D'Amore 
1170371584c2SYuri Pankov 	return 0;
117195c635efSGarrett D'Amore }
117295c635efSGarrett D'Amore 
117395c635efSGarrett D'Amore /*
117495c635efSGarrett D'Amore  * This decides how to assert whitespace before any of the SYNOPSIS set
117595c635efSGarrett D'Amore  * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
117695c635efSGarrett D'Amore  * macro combos).
117795c635efSGarrett D'Amore  */
117895c635efSGarrett D'Amore static void
synopsis_pre(struct termp * p,struct roff_node * n)1179*4d131170SRobert Mustacchi synopsis_pre(struct termp *p, struct roff_node *n)
118095c635efSGarrett D'Amore {
1181*4d131170SRobert Mustacchi 	struct roff_node	*np;
1182*4d131170SRobert Mustacchi 
1183*4d131170SRobert Mustacchi 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
1184*4d131170SRobert Mustacchi 	    (np = roff_node_prev(n)) == NULL)
118595c635efSGarrett D'Amore 		return;
118695c635efSGarrett D'Amore 
118795c635efSGarrett D'Amore 	/*
118895c635efSGarrett D'Amore 	 * If we're the second in a pair of like elements, emit our
118995c635efSGarrett D'Amore 	 * newline and return.  UNLESS we're `Fo', `Fn', `Fn', in which
119095c635efSGarrett D'Amore 	 * case we soldier on.
119195c635efSGarrett D'Amore 	 */
1192*4d131170SRobert Mustacchi 	if (np->tok == n->tok &&
1193260e9a87SYuri Pankov 	    MDOC_Ft != n->tok &&
1194260e9a87SYuri Pankov 	    MDOC_Fo != n->tok &&
1195260e9a87SYuri Pankov 	    MDOC_Fn != n->tok) {
119695c635efSGarrett D'Amore 		term_newln(p);
119795c635efSGarrett D'Amore 		return;
119895c635efSGarrett D'Amore 	}
119995c635efSGarrett D'Amore 
120095c635efSGarrett D'Amore 	/*
120195c635efSGarrett D'Amore 	 * If we're one of the SYNOPSIS set and non-like pair-wise after
120295c635efSGarrett D'Amore 	 * another (or Fn/Fo, which we've let slip through) then assert
120395c635efSGarrett D'Amore 	 * vertical space, else only newline and move on.
120495c635efSGarrett D'Amore 	 */
1205*4d131170SRobert Mustacchi 	switch (np->tok) {
1206260e9a87SYuri Pankov 	case MDOC_Fd:
1207260e9a87SYuri Pankov 	case MDOC_Fn:
1208260e9a87SYuri Pankov 	case MDOC_Fo:
1209260e9a87SYuri Pankov 	case MDOC_In:
1210260e9a87SYuri Pankov 	case MDOC_Vt:
121195c635efSGarrett D'Amore 		term_vspace(p);
121295c635efSGarrett D'Amore 		break;
1213260e9a87SYuri Pankov 	case MDOC_Ft:
1214*4d131170SRobert Mustacchi 		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
121595c635efSGarrett D'Amore 			term_vspace(p);
121695c635efSGarrett D'Amore 			break;
121795c635efSGarrett D'Amore 		}
121895c635efSGarrett D'Amore 		/* FALLTHROUGH */
121995c635efSGarrett D'Amore 	default:
122095c635efSGarrett D'Amore 		term_newln(p);
122195c635efSGarrett D'Amore 		break;
122295c635efSGarrett D'Amore 	}
122395c635efSGarrett D'Amore }
122495c635efSGarrett D'Amore 
122595c635efSGarrett D'Amore static int
termp_vt_pre(DECL_ARGS)122695c635efSGarrett D'Amore termp_vt_pre(DECL_ARGS)
122795c635efSGarrett D'Amore {
1228*4d131170SRobert Mustacchi 	switch (n->type) {
1229*4d131170SRobert Mustacchi 	case ROFFT_ELEM:
1230*4d131170SRobert Mustacchi 		return termp_ft_pre(p, pair, meta, n);
1231*4d131170SRobert Mustacchi 	case ROFFT_BLOCK:
123295c635efSGarrett D'Amore 		synopsis_pre(p, n);
1233371584c2SYuri Pankov 		return 1;
1234*4d131170SRobert Mustacchi 	case ROFFT_HEAD:
1235371584c2SYuri Pankov 		return 0;
1236*4d131170SRobert Mustacchi 	default:
1237*4d131170SRobert Mustacchi 		return termp_under_pre(p, pair, meta, n);
1238*4d131170SRobert Mustacchi 	}
123995c635efSGarrett D'Amore }
124095c635efSGarrett D'Amore 
124195c635efSGarrett D'Amore static int
termp_bold_pre(DECL_ARGS)124295c635efSGarrett D'Amore termp_bold_pre(DECL_ARGS)
124395c635efSGarrett D'Amore {
124495c635efSGarrett D'Amore 	term_fontpush(p, TERMFONT_BOLD);
1245371584c2SYuri Pankov 	return 1;
124695c635efSGarrett D'Amore }
124795c635efSGarrett D'Amore 
124895c635efSGarrett D'Amore static int
termp_fd_pre(DECL_ARGS)124995c635efSGarrett D'Amore termp_fd_pre(DECL_ARGS)
125095c635efSGarrett D'Amore {
125195c635efSGarrett D'Amore 	synopsis_pre(p, n);
1252371584c2SYuri Pankov 	return termp_bold_pre(p, pair, meta, n);
1253698f87a4SGarrett D'Amore }
1254698f87a4SGarrett D'Amore 
1255698f87a4SGarrett D'Amore static void
termp_fd_post(DECL_ARGS)1256698f87a4SGarrett D'Amore termp_fd_post(DECL_ARGS)
1257698f87a4SGarrett D'Amore {
1258698f87a4SGarrett D'Amore 	term_newln(p);
125995c635efSGarrett D'Amore }
126095c635efSGarrett D'Amore 
126195c635efSGarrett D'Amore static int
termp_sh_pre(DECL_ARGS)126295c635efSGarrett D'Amore termp_sh_pre(DECL_ARGS)
126395c635efSGarrett D'Amore {
1264*4d131170SRobert Mustacchi 	struct roff_node	*np;
126595c635efSGarrett D'Amore 
126695c635efSGarrett D'Amore 	switch (n->type) {
1267371584c2SYuri Pankov 	case ROFFT_BLOCK:
1268260e9a87SYuri Pankov 		/*
1269260e9a87SYuri Pankov 		 * Vertical space before sections, except
1270260e9a87SYuri Pankov 		 * when the previous section was empty.
1271260e9a87SYuri Pankov 		 */
1272*4d131170SRobert Mustacchi 		if ((np = roff_node_prev(n)) == NULL ||
1273*4d131170SRobert Mustacchi 		    np->tok != MDOC_Sh ||
1274*4d131170SRobert Mustacchi 		    (np->body != NULL && np->body->child != NULL))
1275260e9a87SYuri Pankov 			term_vspace(p);
127695c635efSGarrett D'Amore 		break;
1277371584c2SYuri Pankov 	case ROFFT_HEAD:
1278*4d131170SRobert Mustacchi 		return termp_bold_pre(p, pair, meta, n);
1279371584c2SYuri Pankov 	case ROFFT_BODY:
1280c66b8046SYuri Pankov 		p->tcol->offset = term_len(p, p->defindent);
1281c66b8046SYuri Pankov 		term_tab_set(p, NULL);
1282c66b8046SYuri Pankov 		term_tab_set(p, "T");
1283c66b8046SYuri Pankov 		term_tab_set(p, ".5i");
1284*4d131170SRobert Mustacchi 		if (n->sec == SEC_AUTHORS)
1285698f87a4SGarrett D'Amore 			p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
128695c635efSGarrett D'Amore 		break;
128795c635efSGarrett D'Amore 	default:
128895c635efSGarrett D'Amore 		break;
128995c635efSGarrett D'Amore 	}
1290371584c2SYuri Pankov 	return 1;
129195c635efSGarrett D'Amore }
129295c635efSGarrett D'Amore 
129395c635efSGarrett D'Amore static void
termp_sh_post(DECL_ARGS)129495c635efSGarrett D'Amore termp_sh_post(DECL_ARGS)
129595c635efSGarrett D'Amore {
129695c635efSGarrett D'Amore 	switch (n->type) {
1297371584c2SYuri Pankov 	case ROFFT_HEAD:
129895c635efSGarrett D'Amore 		term_newln(p);
129995c635efSGarrett D'Amore 		break;
1300371584c2SYuri Pankov 	case ROFFT_BODY:
130195c635efSGarrett D'Amore 		term_newln(p);
1302c66b8046SYuri Pankov 		p->tcol->offset = 0;
130395c635efSGarrett D'Amore 		break;
130495c635efSGarrett D'Amore 	default:
130595c635efSGarrett D'Amore 		break;
130695c635efSGarrett D'Amore 	}
130795c635efSGarrett D'Amore }
130895c635efSGarrett D'Amore 
130995c635efSGarrett D'Amore static void
termp_lb_post(DECL_ARGS)131095c635efSGarrett D'Amore termp_lb_post(DECL_ARGS)
131195c635efSGarrett D'Amore {
1312*4d131170SRobert Mustacchi 	if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE)
131395c635efSGarrett D'Amore 		term_newln(p);
131495c635efSGarrett D'Amore }
131595c635efSGarrett D'Amore 
131695c635efSGarrett D'Amore static int
termp_d1_pre(DECL_ARGS)131795c635efSGarrett D'Amore termp_d1_pre(DECL_ARGS)
131895c635efSGarrett D'Amore {
1319371584c2SYuri Pankov 	if (n->type != ROFFT_BLOCK)
1320371584c2SYuri Pankov 		return 1;
132195c635efSGarrett D'Amore 	term_newln(p);
1322c66b8046SYuri Pankov 	p->tcol->offset += term_len(p, p->defindent + 1);
1323c66b8046SYuri Pankov 	term_tab_set(p, NULL);
1324c66b8046SYuri Pankov 	term_tab_set(p, "T");
1325c66b8046SYuri Pankov 	term_tab_set(p, ".5i");
1326371584c2SYuri Pankov 	return 1;
132795c635efSGarrett D'Amore }
132895c635efSGarrett D'Amore 
132995c635efSGarrett D'Amore static int
termp_ft_pre(DECL_ARGS)133095c635efSGarrett D'Amore termp_ft_pre(DECL_ARGS)
133195c635efSGarrett D'Amore {
133295c635efSGarrett D'Amore 	synopsis_pre(p, n);
1333*4d131170SRobert Mustacchi 	return termp_under_pre(p, pair, meta, n);
133495c635efSGarrett D'Amore }
133595c635efSGarrett D'Amore 
133695c635efSGarrett D'Amore static int
termp_fn_pre(DECL_ARGS)133795c635efSGarrett D'Amore termp_fn_pre(DECL_ARGS)
133895c635efSGarrett D'Amore {
1339698f87a4SGarrett D'Amore 	size_t		 rmargin = 0;
134095c635efSGarrett D'Amore 	int		 pretty;
134195c635efSGarrett D'Amore 
134295c635efSGarrett D'Amore 	synopsis_pre(p, n);
1343*4d131170SRobert Mustacchi 	pretty = n->flags & NODE_SYNPRETTY;
1344*4d131170SRobert Mustacchi 	if ((n = n->child) == NULL)
1345371584c2SYuri Pankov 		return 0;
134695c635efSGarrett D'Amore 
1347698f87a4SGarrett D'Amore 	if (pretty) {
1348c66b8046SYuri Pankov 		rmargin = p->tcol->rmargin;
1349c66b8046SYuri Pankov 		p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1350260e9a87SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1351698f87a4SGarrett D'Amore 	}
1352698f87a4SGarrett D'Amore 
1353371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
135495c635efSGarrett D'Amore 	term_fontpush(p, TERMFONT_BOLD);
135595c635efSGarrett D'Amore 	term_word(p, n->string);
135695c635efSGarrett D'Amore 	term_fontpop(p);
135795c635efSGarrett D'Amore 
1358698f87a4SGarrett D'Amore 	if (pretty) {
1359698f87a4SGarrett D'Amore 		term_flushln(p);
1360260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1361c66b8046SYuri Pankov 		p->flags |= TERMP_NOPAD;
1362c66b8046SYuri Pankov 		p->tcol->offset = p->tcol->rmargin;
1363c66b8046SYuri Pankov 		p->tcol->rmargin = rmargin;
1364698f87a4SGarrett D'Amore 	}
1365698f87a4SGarrett D'Amore 
136695c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
136795c635efSGarrett D'Amore 	term_word(p, "(");
136895c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
136995c635efSGarrett D'Amore 
137095c635efSGarrett D'Amore 	for (n = n->next; n; n = n->next) {
1371371584c2SYuri Pankov 		assert(n->type == ROFFT_TEXT);
137295c635efSGarrett D'Amore 		term_fontpush(p, TERMFONT_UNDER);
1373698f87a4SGarrett D'Amore 		if (pretty)
1374698f87a4SGarrett D'Amore 			p->flags |= TERMP_NBRWORD;
137595c635efSGarrett D'Amore 		term_word(p, n->string);
137695c635efSGarrett D'Amore 		term_fontpop(p);
137795c635efSGarrett D'Amore 
137895c635efSGarrett D'Amore 		if (n->next) {
137995c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
138095c635efSGarrett D'Amore 			term_word(p, ",");
138195c635efSGarrett D'Amore 		}
138295c635efSGarrett D'Amore 	}
138395c635efSGarrett D'Amore 
138495c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
138595c635efSGarrett D'Amore 	term_word(p, ")");
138695c635efSGarrett D'Amore 
138795c635efSGarrett D'Amore 	if (pretty) {
138895c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
138995c635efSGarrett D'Amore 		term_word(p, ";");
1390698f87a4SGarrett D'Amore 		term_flushln(p);
139195c635efSGarrett D'Amore 	}
1392371584c2SYuri Pankov 	return 0;
139395c635efSGarrett D'Amore }
139495c635efSGarrett D'Amore 
139595c635efSGarrett D'Amore static int
termp_fa_pre(DECL_ARGS)139695c635efSGarrett D'Amore termp_fa_pre(DECL_ARGS)
139795c635efSGarrett D'Amore {
1398371584c2SYuri Pankov 	const struct roff_node	*nn;
139995c635efSGarrett D'Amore 
1400*4d131170SRobert Mustacchi 	if (n->parent->tok != MDOC_Fo)
1401*4d131170SRobert Mustacchi 		return termp_under_pre(p, pair, meta, n);
140295c635efSGarrett D'Amore 
1403*4d131170SRobert Mustacchi 	for (nn = n->child; nn != NULL; nn = nn->next) {
140495c635efSGarrett D'Amore 		term_fontpush(p, TERMFONT_UNDER);
1405698f87a4SGarrett D'Amore 		p->flags |= TERMP_NBRWORD;
140695c635efSGarrett D'Amore 		term_word(p, nn->string);
140795c635efSGarrett D'Amore 		term_fontpop(p);
1408*4d131170SRobert Mustacchi 		if (nn->next != NULL) {
140995c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
141095c635efSGarrett D'Amore 			term_word(p, ",");
141195c635efSGarrett D'Amore 		}
141295c635efSGarrett D'Amore 	}
1413*4d131170SRobert Mustacchi 	if (n->child != NULL &&
1414*4d131170SRobert Mustacchi 	    (nn = roff_node_next(n)) != NULL &&
1415*4d131170SRobert Mustacchi 	    nn->tok == MDOC_Fa) {
1416*4d131170SRobert Mustacchi 		p->flags |= TERMP_NOSPACE;
1417*4d131170SRobert Mustacchi 		term_word(p, ",");
1418*4d131170SRobert Mustacchi 	}
1419371584c2SYuri Pankov 	return 0;
142095c635efSGarrett D'Amore }
142195c635efSGarrett D'Amore 
142295c635efSGarrett D'Amore static int
termp_bd_pre(DECL_ARGS)142395c635efSGarrett D'Amore termp_bd_pre(DECL_ARGS)
142495c635efSGarrett D'Amore {
1425260e9a87SYuri Pankov 	int			 offset;
142695c635efSGarrett D'Amore 
1427371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK) {
142895c635efSGarrett D'Amore 		print_bvspace(p, n, n);
1429371584c2SYuri Pankov 		return 1;
1430371584c2SYuri Pankov 	} else if (n->type == ROFFT_HEAD)
1431371584c2SYuri Pankov 		return 0;
143295c635efSGarrett D'Amore 
1433260e9a87SYuri Pankov 	/* Handle the -offset argument. */
1434260e9a87SYuri Pankov 
1435260e9a87SYuri Pankov 	if (n->norm->Bd.offs == NULL ||
1436260e9a87SYuri Pankov 	    ! strcmp(n->norm->Bd.offs, "left"))
1437260e9a87SYuri Pankov 		/* nothing */;
1438260e9a87SYuri Pankov 	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1439c66b8046SYuri Pankov 		p->tcol->offset += term_len(p, p->defindent + 1);
1440260e9a87SYuri Pankov 	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1441c66b8046SYuri Pankov 		p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
1442260e9a87SYuri Pankov 	else {
1443260e9a87SYuri Pankov 		offset = a2width(p, n->norm->Bd.offs);
1444c66b8046SYuri Pankov 		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
1445c66b8046SYuri Pankov 			p->tcol->offset = 0;
1446260e9a87SYuri Pankov 		else if (offset < SHRT_MAX)
1447c66b8046SYuri Pankov 			p->tcol->offset += offset;
1448260e9a87SYuri Pankov 	}
144995c635efSGarrett D'Amore 
1450cec8643bSMichal Nowak 	switch (n->norm->Bd.type) {
1451cec8643bSMichal Nowak 	case DISP_literal:
1452c66b8046SYuri Pankov 		term_tab_set(p, NULL);
1453c66b8046SYuri Pankov 		term_tab_set(p, "T");
1454c66b8046SYuri Pankov 		term_tab_set(p, "8n");
1455cec8643bSMichal Nowak 		break;
1456cec8643bSMichal Nowak 	case DISP_centered:
1457cec8643bSMichal Nowak 		p->flags |= TERMP_CENTER;
1458cec8643bSMichal Nowak 		break;
1459cec8643bSMichal Nowak 	default:
1460cec8643bSMichal Nowak 		break;
1461c66b8046SYuri Pankov 	}
1462cec8643bSMichal Nowak 	return 1;
146395c635efSGarrett D'Amore }
146495c635efSGarrett D'Amore 
146595c635efSGarrett D'Amore static void
termp_bd_post(DECL_ARGS)146695c635efSGarrett D'Amore termp_bd_post(DECL_ARGS)
146795c635efSGarrett D'Amore {
1468371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
146995c635efSGarrett D'Amore 		return;
1470cec8643bSMichal Nowak 	if (n->norm->Bd.type == DISP_unfilled ||
1471cec8643bSMichal Nowak 	    n->norm->Bd.type == DISP_literal)
1472c66b8046SYuri Pankov 		p->flags |= TERMP_BRNEVER;
147395c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
147495c635efSGarrett D'Amore 	term_newln(p);
1475c66b8046SYuri Pankov 	p->flags &= ~TERMP_BRNEVER;
1476cec8643bSMichal Nowak 	if (n->norm->Bd.type == DISP_centered)
1477cec8643bSMichal Nowak 		p->flags &= ~TERMP_CENTER;
147895c635efSGarrett D'Amore }
147995c635efSGarrett D'Amore 
148095c635efSGarrett D'Amore static int
termp_xx_pre(DECL_ARGS)1481a40ea1a7SYuri Pankov termp_xx_pre(DECL_ARGS)
148295c635efSGarrett D'Amore {
1483a40ea1a7SYuri Pankov 	if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1484a40ea1a7SYuri Pankov 		p->flags |= TERMP_PREKEEP;
1485a40ea1a7SYuri Pankov 	return 1;
148695c635efSGarrett D'Amore }
148795c635efSGarrett D'Amore 
1488a40ea1a7SYuri Pankov static void
termp_xx_post(DECL_ARGS)1489a40ea1a7SYuri Pankov termp_xx_post(DECL_ARGS)
149095c635efSGarrett D'Amore {
1491a40ea1a7SYuri Pankov 	if (n->aux == 0)
1492a40ea1a7SYuri Pankov 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
149395c635efSGarrett D'Amore }
149495c635efSGarrett D'Amore 
149595c635efSGarrett D'Amore static void
termp_pf_post(DECL_ARGS)149695c635efSGarrett D'Amore termp_pf_post(DECL_ARGS)
149795c635efSGarrett D'Amore {
1498*4d131170SRobert Mustacchi 	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1499260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE;
150095c635efSGarrett D'Amore }
150195c635efSGarrett D'Amore 
150295c635efSGarrett D'Amore static int
termp_ss_pre(DECL_ARGS)150395c635efSGarrett D'Amore termp_ss_pre(DECL_ARGS)
150495c635efSGarrett D'Amore {
150595c635efSGarrett D'Amore 	switch (n->type) {
1506371584c2SYuri Pankov 	case ROFFT_BLOCK:
1507*4d131170SRobert Mustacchi 		if (roff_node_prev(n) == NULL)
1508*4d131170SRobert Mustacchi 			term_newln(p);
1509*4d131170SRobert Mustacchi 		else
151095c635efSGarrett D'Amore 			term_vspace(p);
151195c635efSGarrett D'Amore 		break;
1512371584c2SYuri Pankov 	case ROFFT_HEAD:
1513c66b8046SYuri Pankov 		p->tcol->offset = term_len(p, (p->defindent+1)/2);
1514*4d131170SRobert Mustacchi 		return termp_bold_pre(p, pair, meta, n);
1515371584c2SYuri Pankov 	case ROFFT_BODY:
1516c66b8046SYuri Pankov 		p->tcol->offset = term_len(p, p->defindent);
1517c66b8046SYuri Pankov 		term_tab_set(p, NULL);
1518c66b8046SYuri Pankov 		term_tab_set(p, "T");
1519c66b8046SYuri Pankov 		term_tab_set(p, ".5i");
1520260e9a87SYuri Pankov 		break;
152195c635efSGarrett D'Amore 	default:
152295c635efSGarrett D'Amore 		break;
152395c635efSGarrett D'Amore 	}
1524371584c2SYuri Pankov 	return 1;
152595c635efSGarrett D'Amore }
152695c635efSGarrett D'Amore 
152795c635efSGarrett D'Amore static void
termp_ss_post(DECL_ARGS)152895c635efSGarrett D'Amore termp_ss_post(DECL_ARGS)
152995c635efSGarrett D'Amore {
1530371584c2SYuri Pankov 	if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
153195c635efSGarrett D'Amore 		term_newln(p);
153295c635efSGarrett D'Amore }
153395c635efSGarrett D'Amore 
153495c635efSGarrett D'Amore static int
termp_in_pre(DECL_ARGS)153595c635efSGarrett D'Amore termp_in_pre(DECL_ARGS)
153695c635efSGarrett D'Amore {
153795c635efSGarrett D'Amore 	synopsis_pre(p, n);
1538*4d131170SRobert Mustacchi 	if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) {
153995c635efSGarrett D'Amore 		term_fontpush(p, TERMFONT_BOLD);
154095c635efSGarrett D'Amore 		term_word(p, "#include");
154195c635efSGarrett D'Amore 		term_word(p, "<");
154295c635efSGarrett D'Amore 	} else {
154395c635efSGarrett D'Amore 		term_word(p, "<");
154495c635efSGarrett D'Amore 		term_fontpush(p, TERMFONT_UNDER);
154595c635efSGarrett D'Amore 	}
154695c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1547371584c2SYuri Pankov 	return 1;
154895c635efSGarrett D'Amore }
154995c635efSGarrett D'Amore 
155095c635efSGarrett D'Amore static void
termp_in_post(DECL_ARGS)155195c635efSGarrett D'Amore termp_in_post(DECL_ARGS)
155295c635efSGarrett D'Amore {
1553*4d131170SRobert Mustacchi 	if (n->flags & NODE_SYNPRETTY)
155495c635efSGarrett D'Amore 		term_fontpush(p, TERMFONT_BOLD);
155595c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
155695c635efSGarrett D'Amore 	term_word(p, ">");
1557*4d131170SRobert Mustacchi 	if (n->flags & NODE_SYNPRETTY)
155895c635efSGarrett D'Amore 		term_fontpop(p);
155995c635efSGarrett D'Amore }
156095c635efSGarrett D'Amore 
156195c635efSGarrett D'Amore static int
termp_pp_pre(DECL_ARGS)1562c66b8046SYuri Pankov termp_pp_pre(DECL_ARGS)
156395c635efSGarrett D'Amore {
1564c66b8046SYuri Pankov 	term_vspace(p);
1565*4d131170SRobert Mustacchi 	if (n->flags & NODE_ID)
1566*4d131170SRobert Mustacchi 		term_tag_write(n, p->line);
1567371584c2SYuri Pankov 	return 0;
156895c635efSGarrett D'Amore }
156995c635efSGarrett D'Amore 
1570260e9a87SYuri Pankov static int
termp_skip_pre(DECL_ARGS)1571260e9a87SYuri Pankov termp_skip_pre(DECL_ARGS)
1572260e9a87SYuri Pankov {
1573371584c2SYuri Pankov 	return 0;
1574260e9a87SYuri Pankov }
157595c635efSGarrett D'Amore 
157695c635efSGarrett D'Amore static int
termp_quote_pre(DECL_ARGS)157795c635efSGarrett D'Amore termp_quote_pre(DECL_ARGS)
157895c635efSGarrett D'Amore {
1579371584c2SYuri Pankov 	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1580371584c2SYuri Pankov 		return 1;
158195c635efSGarrett D'Amore 
158295c635efSGarrett D'Amore 	switch (n->tok) {
1583260e9a87SYuri Pankov 	case MDOC_Ao:
1584260e9a87SYuri Pankov 	case MDOC_Aq:
1585371584c2SYuri Pankov 		term_word(p, n->child != NULL && n->child->next == NULL &&
1586260e9a87SYuri Pankov 		    n->child->tok == MDOC_Mt ? "<" : "\\(la");
158795c635efSGarrett D'Amore 		break;
1588260e9a87SYuri Pankov 	case MDOC_Bro:
1589260e9a87SYuri Pankov 	case MDOC_Brq:
159095c635efSGarrett D'Amore 		term_word(p, "{");
159195c635efSGarrett D'Amore 		break;
1592260e9a87SYuri Pankov 	case MDOC_Oo:
1593260e9a87SYuri Pankov 	case MDOC_Op:
1594260e9a87SYuri Pankov 	case MDOC_Bo:
1595260e9a87SYuri Pankov 	case MDOC_Bq:
159695c635efSGarrett D'Amore 		term_word(p, "[");
159795c635efSGarrett D'Amore 		break;
1598a40ea1a7SYuri Pankov 	case MDOC__T:
1599a40ea1a7SYuri Pankov 		/* FALLTHROUGH */
1600260e9a87SYuri Pankov 	case MDOC_Do:
1601260e9a87SYuri Pankov 	case MDOC_Dq:
16026640c13bSYuri Pankov 		term_word(p, "\\(lq");
160395c635efSGarrett D'Amore 		break;
1604260e9a87SYuri Pankov 	case MDOC_En:
1605260e9a87SYuri Pankov 		if (NULL == n->norm->Es ||
1606260e9a87SYuri Pankov 		    NULL == n->norm->Es->child)
1607371584c2SYuri Pankov 			return 1;
1608260e9a87SYuri Pankov 		term_word(p, n->norm->Es->child->string);
160995c635efSGarrett D'Amore 		break;
1610260e9a87SYuri Pankov 	case MDOC_Po:
1611260e9a87SYuri Pankov 	case MDOC_Pq:
161295c635efSGarrett D'Amore 		term_word(p, "(");
161395c635efSGarrett D'Amore 		break;
1614260e9a87SYuri Pankov 	case MDOC_Qo:
1615260e9a87SYuri Pankov 	case MDOC_Qq:
161695c635efSGarrett D'Amore 		term_word(p, "\"");
161795c635efSGarrett D'Amore 		break;
1618260e9a87SYuri Pankov 	case MDOC_Ql:
1619260e9a87SYuri Pankov 	case MDOC_So:
1620260e9a87SYuri Pankov 	case MDOC_Sq:
1621698f87a4SGarrett D'Amore 		term_word(p, "\\(oq");
162295c635efSGarrett D'Amore 		break;
162395c635efSGarrett D'Amore 	default:
162495c635efSGarrett D'Amore 		abort();
162595c635efSGarrett D'Amore 	}
162695c635efSGarrett D'Amore 
162795c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1628371584c2SYuri Pankov 	return 1;
162995c635efSGarrett D'Amore }
163095c635efSGarrett D'Amore 
163195c635efSGarrett D'Amore static void
termp_quote_post(DECL_ARGS)163295c635efSGarrett D'Amore termp_quote_post(DECL_ARGS)
163395c635efSGarrett D'Amore {
163495c635efSGarrett D'Amore 
1635371584c2SYuri Pankov 	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
163695c635efSGarrett D'Amore 		return;
163795c635efSGarrett D'Amore 
163895c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
163995c635efSGarrett D'Amore 
164095c635efSGarrett D'Amore 	switch (n->tok) {
1641260e9a87SYuri Pankov 	case MDOC_Ao:
1642260e9a87SYuri Pankov 	case MDOC_Aq:
1643371584c2SYuri Pankov 		term_word(p, n->child != NULL && n->child->next == NULL &&
1644260e9a87SYuri Pankov 		    n->child->tok == MDOC_Mt ? ">" : "\\(ra");
164595c635efSGarrett D'Amore 		break;
1646260e9a87SYuri Pankov 	case MDOC_Bro:
1647260e9a87SYuri Pankov 	case MDOC_Brq:
164895c635efSGarrett D'Amore 		term_word(p, "}");
164995c635efSGarrett D'Amore 		break;
1650260e9a87SYuri Pankov 	case MDOC_Oo:
1651260e9a87SYuri Pankov 	case MDOC_Op:
1652260e9a87SYuri Pankov 	case MDOC_Bo:
1653260e9a87SYuri Pankov 	case MDOC_Bq:
165495c635efSGarrett D'Amore 		term_word(p, "]");
165595c635efSGarrett D'Amore 		break;
1656a40ea1a7SYuri Pankov 	case MDOC__T:
1657a40ea1a7SYuri Pankov 		/* FALLTHROUGH */
1658260e9a87SYuri Pankov 	case MDOC_Do:
1659260e9a87SYuri Pankov 	case MDOC_Dq:
16606640c13bSYuri Pankov 		term_word(p, "\\(rq");
166195c635efSGarrett D'Amore 		break;
1662260e9a87SYuri Pankov 	case MDOC_En:
1663260e9a87SYuri Pankov 		if (n->norm->Es == NULL ||
1664260e9a87SYuri Pankov 		    n->norm->Es->child == NULL ||
1665260e9a87SYuri Pankov 		    n->norm->Es->child->next == NULL)
1666260e9a87SYuri Pankov 			p->flags &= ~TERMP_NOSPACE;
1667260e9a87SYuri Pankov 		else
1668260e9a87SYuri Pankov 			term_word(p, n->norm->Es->child->next->string);
166995c635efSGarrett D'Amore 		break;
1670260e9a87SYuri Pankov 	case MDOC_Po:
1671260e9a87SYuri Pankov 	case MDOC_Pq:
167295c635efSGarrett D'Amore 		term_word(p, ")");
167395c635efSGarrett D'Amore 		break;
1674260e9a87SYuri Pankov 	case MDOC_Qo:
1675260e9a87SYuri Pankov 	case MDOC_Qq:
167695c635efSGarrett D'Amore 		term_word(p, "\"");
167795c635efSGarrett D'Amore 		break;
1678260e9a87SYuri Pankov 	case MDOC_Ql:
1679260e9a87SYuri Pankov 	case MDOC_So:
1680260e9a87SYuri Pankov 	case MDOC_Sq:
1681698f87a4SGarrett D'Amore 		term_word(p, "\\(cq");
168295c635efSGarrett D'Amore 		break;
168395c635efSGarrett D'Amore 	default:
168495c635efSGarrett D'Amore 		abort();
168595c635efSGarrett D'Amore 	}
168695c635efSGarrett D'Amore }
168795c635efSGarrett D'Amore 
1688260e9a87SYuri Pankov static int
termp_eo_pre(DECL_ARGS)1689260e9a87SYuri Pankov termp_eo_pre(DECL_ARGS)
1690260e9a87SYuri Pankov {
1691260e9a87SYuri Pankov 
1692371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
1693371584c2SYuri Pankov 		return 1;
1694260e9a87SYuri Pankov 
1695260e9a87SYuri Pankov 	if (n->end == ENDBODY_NOT &&
1696260e9a87SYuri Pankov 	    n->parent->head->child == NULL &&
1697260e9a87SYuri Pankov 	    n->child != NULL &&
1698260e9a87SYuri Pankov 	    n->child->end != ENDBODY_NOT)
1699260e9a87SYuri Pankov 		term_word(p, "\\&");
1700260e9a87SYuri Pankov 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1701260e9a87SYuri Pankov 	     n->parent->head->child != NULL && (n->child != NULL ||
1702260e9a87SYuri Pankov 	     (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1703260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE;
1704260e9a87SYuri Pankov 
1705371584c2SYuri Pankov 	return 1;
1706260e9a87SYuri Pankov }
1707260e9a87SYuri Pankov 
1708260e9a87SYuri Pankov static void
termp_eo_post(DECL_ARGS)1709260e9a87SYuri Pankov termp_eo_post(DECL_ARGS)
1710260e9a87SYuri Pankov {
1711260e9a87SYuri Pankov 	int	 body, tail;
1712260e9a87SYuri Pankov 
1713371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
1714260e9a87SYuri Pankov 		return;
1715260e9a87SYuri Pankov 
1716260e9a87SYuri Pankov 	if (n->end != ENDBODY_NOT) {
1717260e9a87SYuri Pankov 		p->flags &= ~TERMP_NOSPACE;
1718260e9a87SYuri Pankov 		return;
1719260e9a87SYuri Pankov 	}
1720260e9a87SYuri Pankov 
1721260e9a87SYuri Pankov 	body = n->child != NULL || n->parent->head->child != NULL;
1722260e9a87SYuri Pankov 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1723260e9a87SYuri Pankov 
1724260e9a87SYuri Pankov 	if (body && tail)
1725260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE;
1726260e9a87SYuri Pankov 	else if ( ! (body || tail))
1727260e9a87SYuri Pankov 		term_word(p, "\\&");
1728260e9a87SYuri Pankov 	else if ( ! tail)
1729260e9a87SYuri Pankov 		p->flags &= ~TERMP_NOSPACE;
1730260e9a87SYuri Pankov }
173195c635efSGarrett D'Amore 
173295c635efSGarrett D'Amore static int
termp_fo_pre(DECL_ARGS)173395c635efSGarrett D'Amore termp_fo_pre(DECL_ARGS)
173495c635efSGarrett D'Amore {
1735*4d131170SRobert Mustacchi 	size_t rmargin;
173695c635efSGarrett D'Amore 
1737*4d131170SRobert Mustacchi 	switch (n->type) {
1738*4d131170SRobert Mustacchi 	case ROFFT_BLOCK:
173995c635efSGarrett D'Amore 		synopsis_pre(p, n);
1740371584c2SYuri Pankov 		return 1;
1741*4d131170SRobert Mustacchi 	case ROFFT_BODY:
1742*4d131170SRobert Mustacchi 		rmargin = p->tcol->rmargin;
1743*4d131170SRobert Mustacchi 		if (n->flags & NODE_SYNPRETTY) {
1744c66b8046SYuri Pankov 			p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1745260e9a87SYuri Pankov 			p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1746260e9a87SYuri Pankov 					TERMP_HANG;
1747698f87a4SGarrett D'Amore 		}
174895c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
174995c635efSGarrett D'Amore 		term_word(p, "(");
175095c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
1751*4d131170SRobert Mustacchi 		if (n->flags & NODE_SYNPRETTY) {
1752698f87a4SGarrett D'Amore 			term_flushln(p);
1753260e9a87SYuri Pankov 			p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1754260e9a87SYuri Pankov 					TERMP_HANG);
1755c66b8046SYuri Pankov 			p->flags |= TERMP_NOPAD;
1756c66b8046SYuri Pankov 			p->tcol->offset = p->tcol->rmargin;
1757c66b8046SYuri Pankov 			p->tcol->rmargin = rmargin;
1758698f87a4SGarrett D'Amore 		}
1759371584c2SYuri Pankov 		return 1;
1760*4d131170SRobert Mustacchi 	default:
1761*4d131170SRobert Mustacchi 		return termp_bold_pre(p, pair, meta, n);
1762698f87a4SGarrett D'Amore 	}
176395c635efSGarrett D'Amore }
176495c635efSGarrett D'Amore 
176595c635efSGarrett D'Amore static void
termp_fo_post(DECL_ARGS)176695c635efSGarrett D'Amore termp_fo_post(DECL_ARGS)
176795c635efSGarrett D'Amore {
1768371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
176995c635efSGarrett D'Amore 		return;
177095c635efSGarrett D'Amore 
177195c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
177295c635efSGarrett D'Amore 	term_word(p, ")");
177395c635efSGarrett D'Amore 
1774*4d131170SRobert Mustacchi 	if (n->flags & NODE_SYNPRETTY) {
177595c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
177695c635efSGarrett D'Amore 		term_word(p, ";");
1777698f87a4SGarrett D'Amore 		term_flushln(p);
177895c635efSGarrett D'Amore 	}
177995c635efSGarrett D'Amore }
178095c635efSGarrett D'Amore 
178195c635efSGarrett D'Amore static int
termp_bf_pre(DECL_ARGS)178295c635efSGarrett D'Amore termp_bf_pre(DECL_ARGS)
178395c635efSGarrett D'Amore {
1784*4d131170SRobert Mustacchi 	switch (n->type) {
1785*4d131170SRobert Mustacchi 	case ROFFT_HEAD:
1786371584c2SYuri Pankov 		return 0;
1787*4d131170SRobert Mustacchi 	case ROFFT_BODY:
1788*4d131170SRobert Mustacchi 		break;
1789*4d131170SRobert Mustacchi 	default:
1790371584c2SYuri Pankov 		return 1;
1791*4d131170SRobert Mustacchi 	}
1792*4d131170SRobert Mustacchi 	switch (n->norm->Bf.font) {
1793*4d131170SRobert Mustacchi 	case FONT_Em:
1794*4d131170SRobert Mustacchi 		return termp_under_pre(p, pair, meta, n);
1795*4d131170SRobert Mustacchi 	case FONT_Sy:
1796*4d131170SRobert Mustacchi 		return termp_bold_pre(p, pair, meta, n);
1797*4d131170SRobert Mustacchi 	default:
1798*4d131170SRobert Mustacchi 		return termp_li_pre(p, pair, meta, n);
1799*4d131170SRobert Mustacchi 	}
180095c635efSGarrett D'Amore }
180195c635efSGarrett D'Amore 
180295c635efSGarrett D'Amore static int
termp_sm_pre(DECL_ARGS)180395c635efSGarrett D'Amore termp_sm_pre(DECL_ARGS)
180495c635efSGarrett D'Amore {
1805*4d131170SRobert Mustacchi 	if (n->child == NULL)
1806260e9a87SYuri Pankov 		p->flags ^= TERMP_NONOSPACE;
1807*4d131170SRobert Mustacchi 	else if (strcmp(n->child->string, "on") == 0)
180895c635efSGarrett D'Amore 		p->flags &= ~TERMP_NONOSPACE;
1809260e9a87SYuri Pankov 	else
181095c635efSGarrett D'Amore 		p->flags |= TERMP_NONOSPACE;
181195c635efSGarrett D'Amore 
1812260e9a87SYuri Pankov 	if (p->col && ! (TERMP_NONOSPACE & p->flags))
1813260e9a87SYuri Pankov 		p->flags &= ~TERMP_NOSPACE;
1814260e9a87SYuri Pankov 
1815371584c2SYuri Pankov 	return 0;
181695c635efSGarrett D'Amore }
181795c635efSGarrett D'Amore 
181895c635efSGarrett D'Amore static int
termp_ap_pre(DECL_ARGS)181995c635efSGarrett D'Amore termp_ap_pre(DECL_ARGS)
182095c635efSGarrett D'Amore {
182195c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
182295c635efSGarrett D'Amore 	term_word(p, "'");
182395c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1824371584c2SYuri Pankov 	return 1;
182595c635efSGarrett D'Amore }
182695c635efSGarrett D'Amore 
182795c635efSGarrett D'Amore static void
termp____post(DECL_ARGS)182895c635efSGarrett D'Amore termp____post(DECL_ARGS)
182995c635efSGarrett D'Amore {
1830*4d131170SRobert Mustacchi 	struct roff_node *nn;
183195c635efSGarrett D'Amore 
183295c635efSGarrett D'Amore 	/*
183395c635efSGarrett D'Amore 	 * Handle lists of authors.  In general, print each followed by
183495c635efSGarrett D'Amore 	 * a comma.  Don't print the comma if there are only two
183595c635efSGarrett D'Amore 	 * authors.
183695c635efSGarrett D'Amore 	 */
1837*4d131170SRobert Mustacchi 	if (n->tok == MDOC__A &&
1838*4d131170SRobert Mustacchi 	    (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A &&
1839*4d131170SRobert Mustacchi 	    ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) &&
1840*4d131170SRobert Mustacchi 	    ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A))
1841*4d131170SRobert Mustacchi 		return;
184295c635efSGarrett D'Amore 
184395c635efSGarrett D'Amore 	/* TODO: %U. */
184495c635efSGarrett D'Amore 
1845*4d131170SRobert Mustacchi 	if (n->parent == NULL || n->parent->tok != MDOC_Rs)
184695c635efSGarrett D'Amore 		return;
184795c635efSGarrett D'Amore 
184895c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1849*4d131170SRobert Mustacchi 	if (roff_node_next(n) == NULL) {
185095c635efSGarrett D'Amore 		term_word(p, ".");
185195c635efSGarrett D'Amore 		p->flags |= TERMP_SENTENCE;
185295c635efSGarrett D'Amore 	} else
185395c635efSGarrett D'Amore 		term_word(p, ",");
185495c635efSGarrett D'Amore }
185595c635efSGarrett D'Amore 
185695c635efSGarrett D'Amore static int
termp_li_pre(DECL_ARGS)185795c635efSGarrett D'Amore termp_li_pre(DECL_ARGS)
185895c635efSGarrett D'Amore {
185995c635efSGarrett D'Amore 	term_fontpush(p, TERMFONT_NONE);
1860371584c2SYuri Pankov 	return 1;
186195c635efSGarrett D'Amore }
186295c635efSGarrett D'Amore 
186395c635efSGarrett D'Amore static int
termp_lk_pre(DECL_ARGS)186495c635efSGarrett D'Amore termp_lk_pre(DECL_ARGS)
186595c635efSGarrett D'Amore {
1866c66b8046SYuri Pankov 	const struct roff_node *link, *descr, *punct;
186795c635efSGarrett D'Amore 
1868c66b8046SYuri Pankov 	if ((link = n->child) == NULL)
1869371584c2SYuri Pankov 		return 0;
187095c635efSGarrett D'Amore 
1871c66b8046SYuri Pankov 	/* Find beginning of trailing punctuation. */
1872c66b8046SYuri Pankov 	punct = n->last;
1873c66b8046SYuri Pankov 	while (punct != link && punct->flags & NODE_DELIMC)
1874c66b8046SYuri Pankov 		punct = punct->prev;
1875c66b8046SYuri Pankov 	punct = punct->next;
1876c66b8046SYuri Pankov 
1877c66b8046SYuri Pankov 	/* Link text. */
1878c66b8046SYuri Pankov 	if ((descr = link->next) != NULL && descr != punct) {
1879698f87a4SGarrett D'Amore 		term_fontpush(p, TERMFONT_UNDER);
1880c66b8046SYuri Pankov 		while (descr != punct) {
1881c66b8046SYuri Pankov 			if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1882c66b8046SYuri Pankov 				p->flags |= TERMP_NOSPACE;
1883698f87a4SGarrett D'Amore 			term_word(p, descr->string);
1884698f87a4SGarrett D'Amore 			descr = descr->next;
1885698f87a4SGarrett D'Amore 		}
1886c66b8046SYuri Pankov 		term_fontpop(p);
1887698f87a4SGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
1888698f87a4SGarrett D'Amore 		term_word(p, ":");
1889698f87a4SGarrett D'Amore 	}
189095c635efSGarrett D'Amore 
1891c66b8046SYuri Pankov 	/* Link target. */
189295c635efSGarrett D'Amore 	term_fontpush(p, TERMFONT_BOLD);
1893698f87a4SGarrett D'Amore 	term_word(p, link->string);
189495c635efSGarrett D'Amore 	term_fontpop(p);
189595c635efSGarrett D'Amore 
1896c66b8046SYuri Pankov 	/* Trailing punctuation. */
1897c66b8046SYuri Pankov 	while (punct != NULL) {
1898c66b8046SYuri Pankov 		p->flags |= TERMP_NOSPACE;
1899c66b8046SYuri Pankov 		term_word(p, punct->string);
1900c66b8046SYuri Pankov 		punct = punct->next;
1901c66b8046SYuri Pankov 	}
1902371584c2SYuri Pankov 	return 0;
190395c635efSGarrett D'Amore }
190495c635efSGarrett D'Amore 
190595c635efSGarrett D'Amore static int
termp_bk_pre(DECL_ARGS)190695c635efSGarrett D'Amore termp_bk_pre(DECL_ARGS)
190795c635efSGarrett D'Amore {
190895c635efSGarrett D'Amore 	switch (n->type) {
1909371584c2SYuri Pankov 	case ROFFT_BLOCK:
191095c635efSGarrett D'Amore 		break;
1911371584c2SYuri Pankov 	case ROFFT_HEAD:
1912371584c2SYuri Pankov 		return 0;
1913371584c2SYuri Pankov 	case ROFFT_BODY:
1914371584c2SYuri Pankov 		if (n->parent->args != NULL || n->prev->child == NULL)
191595c635efSGarrett D'Amore 			p->flags |= TERMP_PREKEEP;
191695c635efSGarrett D'Amore 		break;
191795c635efSGarrett D'Amore 	default:
191895c635efSGarrett D'Amore 		abort();
191995c635efSGarrett D'Amore 	}
1920371584c2SYuri Pankov 	return 1;
192195c635efSGarrett D'Amore }
192295c635efSGarrett D'Amore 
192395c635efSGarrett D'Amore static void
termp_bk_post(DECL_ARGS)192495c635efSGarrett D'Amore termp_bk_post(DECL_ARGS)
192595c635efSGarrett D'Amore {
1926371584c2SYuri Pankov 	if (n->type == ROFFT_BODY)
192795c635efSGarrett D'Amore 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
192895c635efSGarrett D'Amore }
192995c635efSGarrett D'Amore 
1930*4d131170SRobert Mustacchi /*
1931*4d131170SRobert Mustacchi  * If we are in an `Rs' and there is a journal present,
1932*4d131170SRobert Mustacchi  * then quote us instead of underlining us (for disambiguation).
1933*4d131170SRobert Mustacchi  */
193495c635efSGarrett D'Amore static void
termp__t_post(DECL_ARGS)193595c635efSGarrett D'Amore termp__t_post(DECL_ARGS)
193695c635efSGarrett D'Amore {
1937*4d131170SRobert Mustacchi 	if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
1938260e9a87SYuri Pankov 	    n->parent->norm->Rs.quote_T)
1939698f87a4SGarrett D'Amore 		termp_quote_post(p, pair, meta, n);
1940698f87a4SGarrett D'Amore 	termp____post(p, pair, meta, n);
194195c635efSGarrett D'Amore }
194295c635efSGarrett D'Amore 
194395c635efSGarrett D'Amore static int
termp__t_pre(DECL_ARGS)194495c635efSGarrett D'Amore termp__t_pre(DECL_ARGS)
194595c635efSGarrett D'Amore {
1946*4d131170SRobert Mustacchi 	if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
1947260e9a87SYuri Pankov 	    n->parent->norm->Rs.quote_T)
1948371584c2SYuri Pankov 		return termp_quote_pre(p, pair, meta, n);
1949*4d131170SRobert Mustacchi 	else
1950*4d131170SRobert Mustacchi 		return termp_under_pre(p, pair, meta, n);
195195c635efSGarrett D'Amore }
195295c635efSGarrett D'Amore 
195395c635efSGarrett D'Amore static int
termp_under_pre(DECL_ARGS)195495c635efSGarrett D'Amore termp_under_pre(DECL_ARGS)
195595c635efSGarrett D'Amore {
1956a40ea1a7SYuri Pankov 	term_fontpush(p, TERMFONT_UNDER);
1957a40ea1a7SYuri Pankov 	return 1;
1958a40ea1a7SYuri Pankov }
1959a40ea1a7SYuri Pankov 
1960cec8643bSMichal Nowak static int
termp_abort_pre(DECL_ARGS)1961cec8643bSMichal Nowak termp_abort_pre(DECL_ARGS)
1962cec8643bSMichal Nowak {
1963cec8643bSMichal Nowak 	abort();
1964cec8643bSMichal Nowak }
1965