xref: /openbsd-src/usr.bin/mandoc/mdoc_term.c (revision 84680f53fcf50f9c0d74c7e6cd5986183774cd75)
1*84680f53Sschwarze /* $OpenBSD: mdoc_term.c,v 1.282 2023/11/13 19:13:00 schwarze Exp $ */
2f73abda9Skristaps /*
318bbf166Sschwarze  * Copyright (c) 2010, 2012-2020, 2022 Ingo Schwarze <schwarze@openbsd.org>
40ac7e6ecSschwarze  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5740f368bSschwarze  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
6f73abda9Skristaps  *
7f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps  *
11d1982c71Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d1982c71Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180ac7e6ecSschwarze  *
190ac7e6ecSschwarze  * Plain text formatter for mdoc(7), used by mandoc(1)
200ac7e6ecSschwarze  * for ASCII, UTF-8, PostScript, and PDF output.
21f73abda9Skristaps  */
22f73abda9Skristaps #include <sys/types.h>
23f73abda9Skristaps 
24f73abda9Skristaps #include <assert.h>
25f73abda9Skristaps #include <ctype.h>
2625da2733Sschwarze #include <limits.h>
27fccfce9dSschwarze #include <stdint.h>
28f73abda9Skristaps #include <stdio.h>
29f73abda9Skristaps #include <stdlib.h>
30f73abda9Skristaps #include <string.h>
31f73abda9Skristaps 
32a92c1cd8Sschwarze #include "mandoc_aux.h"
33d1982c71Sschwarze #include "roff.h"
34d1982c71Sschwarze #include "mdoc.h"
354175bdabSschwarze #include "out.h"
36f73abda9Skristaps #include "term.h"
370ac7e6ecSschwarze #include "term_tag.h"
384175bdabSschwarze #include "main.h"
39a231c1b4Sschwarze 
40f73abda9Skristaps struct	termpair {
41f73abda9Skristaps 	struct termpair	 *ppair;
42796a3999Sschwarze 	int		  count;
43f73abda9Skristaps };
44f73abda9Skristaps 
45d6cd8bdcSschwarze #define	DECL_ARGS struct termp *p, \
46d6cd8bdcSschwarze 		  struct termpair *pair, \
472a238f45Sschwarze 		  const struct roff_meta *meta, \
483a0d07afSschwarze 		  struct roff_node *n
49f73abda9Skristaps 
5016fe0cfcSschwarze struct	mdoc_term_act {
51f73abda9Skristaps 	int	(*pre)(DECL_ARGS);
52f73abda9Skristaps 	void	(*post)(DECL_ARGS);
53f73abda9Skristaps };
54f73abda9Skristaps 
5525da2733Sschwarze static	int	  a2width(const struct termp *, const char *);
56b822ca0dSschwarze 
57b822ca0dSschwarze static	void	  print_bvspace(struct termp *,
587ebbefbeSschwarze 			struct roff_node *, struct roff_node *);
59fa70b73eSschwarze static	void	  print_mdoc_node(DECL_ARGS);
60fa70b73eSschwarze static	void	  print_mdoc_nodelist(DECL_ARGS);
612a238f45Sschwarze static	void	  print_mdoc_head(struct termp *, const struct roff_meta *);
622a238f45Sschwarze static	void	  print_mdoc_foot(struct termp *, const struct roff_meta *);
637ebbefbeSschwarze static	void	  synopsis_pre(struct termp *, struct roff_node *);
64b822ca0dSschwarze 
65d6cd8bdcSschwarze static	void	  termp____post(DECL_ARGS);
666f207799Sschwarze static	void	  termp__t_post(DECL_ARGS);
67d6cd8bdcSschwarze static	void	  termp_bd_post(DECL_ARGS);
681eead9a6Sschwarze static	void	  termp_bk_post(DECL_ARGS);
69d6cd8bdcSschwarze static	void	  termp_bl_post(DECL_ARGS);
7003ed6ec9Sschwarze static	void	  termp_eo_post(DECL_ARGS);
71d97bd30dSschwarze static	void	  termp_fd_post(DECL_ARGS);
72d6cd8bdcSschwarze static	void	  termp_fo_post(DECL_ARGS);
73d6cd8bdcSschwarze static	void	  termp_in_post(DECL_ARGS);
74d6cd8bdcSschwarze static	void	  termp_it_post(DECL_ARGS);
75d6cd8bdcSschwarze static	void	  termp_lb_post(DECL_ARGS);
762ab36204Sschwarze static	void	  termp_nm_post(DECL_ARGS);
77d6cd8bdcSschwarze static	void	  termp_pf_post(DECL_ARGS);
782c3450fcSschwarze static	void	  termp_quote_post(DECL_ARGS);
79d6cd8bdcSschwarze static	void	  termp_sh_post(DECL_ARGS);
80d6cd8bdcSschwarze static	void	  termp_ss_post(DECL_ARGS);
81816c3c54Sschwarze static	void	  termp_xx_post(DECL_ARGS);
82d6cd8bdcSschwarze 
83bf782e42Sschwarze static	int	  termp__a_pre(DECL_ARGS);
846f207799Sschwarze static	int	  termp__t_pre(DECL_ARGS);
857c539ecbSschwarze static	int	  termp_abort_pre(DECL_ARGS);
868f46e13cSschwarze static	int	  termp_an_pre(DECL_ARGS);
87d6cd8bdcSschwarze static	int	  termp_ap_pre(DECL_ARGS);
88d6cd8bdcSschwarze static	int	  termp_bd_pre(DECL_ARGS);
89d6cd8bdcSschwarze static	int	  termp_bf_pre(DECL_ARGS);
901eead9a6Sschwarze static	int	  termp_bk_pre(DECL_ARGS);
91b16e7ddfSschwarze static	int	  termp_bl_pre(DECL_ARGS);
92a231c1b4Sschwarze static	int	  termp_bold_pre(DECL_ARGS);
93d6cd8bdcSschwarze static	int	  termp_d1_pre(DECL_ARGS);
9403ed6ec9Sschwarze static	int	  termp_eo_pre(DECL_ARGS);
95d6cd8bdcSschwarze static	int	  termp_ex_pre(DECL_ARGS);
96d6cd8bdcSschwarze static	int	  termp_fa_pre(DECL_ARGS);
972c3450fcSschwarze static	int	  termp_fd_pre(DECL_ARGS);
98d6cd8bdcSschwarze static	int	  termp_fl_pre(DECL_ARGS);
99d6cd8bdcSschwarze static	int	  termp_fn_pre(DECL_ARGS);
100d6cd8bdcSschwarze static	int	  termp_fo_pre(DECL_ARGS);
101d6cd8bdcSschwarze static	int	  termp_ft_pre(DECL_ARGS);
102d6cd8bdcSschwarze static	int	  termp_in_pre(DECL_ARGS);
103d6cd8bdcSschwarze static	int	  termp_it_pre(DECL_ARGS);
104fa70b73eSschwarze static	int	  termp_li_pre(DECL_ARGS);
105d6cd8bdcSschwarze static	int	  termp_lk_pre(DECL_ARGS);
106d6cd8bdcSschwarze static	int	  termp_nd_pre(DECL_ARGS);
107d6cd8bdcSschwarze static	int	  termp_nm_pre(DECL_ARGS);
108d6cd8bdcSschwarze static	int	  termp_ns_pre(DECL_ARGS);
1092c3450fcSschwarze static	int	  termp_quote_pre(DECL_ARGS);
110d6cd8bdcSschwarze static	int	  termp_rs_pre(DECL_ARGS);
111d6cd8bdcSschwarze static	int	  termp_sh_pre(DECL_ARGS);
11278bbbab4Sschwarze static	int	  termp_skip_pre(DECL_ARGS);
113d6cd8bdcSschwarze static	int	  termp_sm_pre(DECL_ARGS);
1146561cb23Sschwarze static	int	  termp_pp_pre(DECL_ARGS);
115d6cd8bdcSschwarze static	int	  termp_ss_pre(DECL_ARGS);
116a231c1b4Sschwarze static	int	  termp_under_pre(DECL_ARGS);
1178521b0bcSschwarze static	int	  termp_vt_pre(DECL_ARGS);
118d6cd8bdcSschwarze static	int	  termp_xr_pre(DECL_ARGS);
119d6cd8bdcSschwarze static	int	  termp_xx_pre(DECL_ARGS);
120d6cd8bdcSschwarze 
12116fe0cfcSschwarze static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
122f73abda9Skristaps 	{ NULL, NULL }, /* Dd */
123f73abda9Skristaps 	{ NULL, NULL }, /* Dt */
124f73abda9Skristaps 	{ NULL, NULL }, /* Os */
125f73abda9Skristaps 	{ termp_sh_pre, termp_sh_post }, /* Sh */
126f73abda9Skristaps 	{ termp_ss_pre, termp_ss_post }, /* Ss */
1276561cb23Sschwarze 	{ termp_pp_pre, NULL }, /* Pp */
128d97bd30dSschwarze 	{ termp_d1_pre, termp_bl_post }, /* D1 */
129d97bd30dSschwarze 	{ termp_d1_pre, termp_bl_post }, /* Dl */
130f73abda9Skristaps 	{ termp_bd_pre, termp_bd_post }, /* Bd */
131f73abda9Skristaps 	{ NULL, NULL }, /* Ed */
132b16e7ddfSschwarze 	{ termp_bl_pre, termp_bl_post }, /* Bl */
133f73abda9Skristaps 	{ NULL, NULL }, /* El */
134f73abda9Skristaps 	{ termp_it_pre, termp_it_post }, /* It */
1358cd724fbSschwarze 	{ termp_under_pre, NULL }, /* Ad */
1363eeea6b8Sschwarze 	{ termp_an_pre, NULL }, /* An */
13714a309e3Sschwarze 	{ termp_ap_pre, NULL }, /* Ap */
138a231c1b4Sschwarze 	{ termp_under_pre, NULL }, /* Ar */
1390ac7e6ecSschwarze 	{ termp_fd_pre, NULL }, /* Cd */
140a231c1b4Sschwarze 	{ termp_bold_pre, NULL }, /* Cm */
141e9698bccSschwarze 	{ termp_li_pre, NULL }, /* Dv */
1420ac7e6ecSschwarze 	{ NULL, NULL }, /* Er */
1430ac7e6ecSschwarze 	{ NULL, NULL }, /* Ev */
144f73abda9Skristaps 	{ termp_ex_pre, NULL }, /* Ex */
145f73abda9Skristaps 	{ termp_fa_pre, NULL }, /* Fa */
146d97bd30dSschwarze 	{ termp_fd_pre, termp_fd_post }, /* Fd */
147f73abda9Skristaps 	{ termp_fl_pre, NULL }, /* Fl */
1489a98b8a1Sschwarze 	{ termp_fn_pre, NULL }, /* Fn */
1496093755cSschwarze 	{ termp_ft_pre, NULL }, /* Ft */
150a231c1b4Sschwarze 	{ termp_bold_pre, NULL }, /* Ic */
151f73abda9Skristaps 	{ termp_in_pre, termp_in_post }, /* In */
152fa70b73eSschwarze 	{ termp_li_pre, NULL }, /* Li */
153f73abda9Skristaps 	{ termp_nd_pre, NULL }, /* Nd */
1542ab36204Sschwarze 	{ termp_nm_pre, termp_nm_post }, /* Nm */
1552c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Op */
1567c539ecbSschwarze 	{ termp_abort_pre, NULL }, /* Ot */
157a231c1b4Sschwarze 	{ termp_under_pre, NULL }, /* Pa */
1588ccddcd3Sschwarze 	{ termp_ex_pre, NULL }, /* Rv */
15971719887Sschwarze 	{ NULL, NULL }, /* St */
160a231c1b4Sschwarze 	{ termp_under_pre, NULL }, /* Va */
1619a98b8a1Sschwarze 	{ termp_vt_pre, NULL }, /* Vt */
162f73abda9Skristaps 	{ termp_xr_pre, NULL }, /* Xr */
163bf782e42Sschwarze 	{ termp__a_pre, termp____post }, /* %A */
164011fe33bSschwarze 	{ termp_under_pre, termp____post }, /* %B */
165f73abda9Skristaps 	{ NULL, termp____post }, /* %D */
166011fe33bSschwarze 	{ termp_under_pre, termp____post }, /* %I */
167a231c1b4Sschwarze 	{ termp_under_pre, termp____post }, /* %J */
168f73abda9Skristaps 	{ NULL, termp____post }, /* %N */
169f73abda9Skristaps 	{ NULL, termp____post }, /* %O */
170f73abda9Skristaps 	{ NULL, termp____post }, /* %P */
171f73abda9Skristaps 	{ NULL, termp____post }, /* %R */
1726f207799Sschwarze 	{ termp__t_pre, termp__t_post }, /* %T */
173f73abda9Skristaps 	{ NULL, termp____post }, /* %V */
174f73abda9Skristaps 	{ NULL, NULL }, /* Ac */
1752c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Ao */
1762c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Aq */
17771719887Sschwarze 	{ NULL, NULL }, /* At */
178f73abda9Skristaps 	{ NULL, NULL }, /* Bc */
179f73abda9Skristaps 	{ termp_bf_pre, NULL }, /* Bf */
1802c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Bo */
1812c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Bq */
182816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Bsx */
1833af8e8d7Sschwarze 	{ NULL, NULL }, /* Bx */
18478bbbab4Sschwarze 	{ termp_skip_pre, NULL }, /* Db */
185f73abda9Skristaps 	{ NULL, NULL }, /* Dc */
1862c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Do */
1872c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Dq */
188b16e7ddfSschwarze 	{ NULL, NULL }, /* Ec */ /* FIXME: no space */
189f73abda9Skristaps 	{ NULL, NULL }, /* Ef */
1900ac7e6ecSschwarze 	{ termp_under_pre, NULL }, /* Em */
19103ed6ec9Sschwarze 	{ termp_eo_pre, termp_eo_post }, /* Eo */
192816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Fx */
193ddce0b0cSschwarze 	{ termp_bold_pre, NULL }, /* Ms */
1946f9818f6Sschwarze 	{ termp_li_pre, NULL }, /* No */
195f73abda9Skristaps 	{ termp_ns_pre, NULL }, /* Ns */
196816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Nx */
197816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Ox */
198f73abda9Skristaps 	{ NULL, NULL }, /* Pc */
199072a1c5dSschwarze 	{ NULL, termp_pf_post }, /* Pf */
2002c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Po */
2012c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Pq */
202f73abda9Skristaps 	{ NULL, NULL }, /* Qc */
2032c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Ql */
2042c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Qo */
2052c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Qq */
206f73abda9Skristaps 	{ NULL, NULL }, /* Re */
207f73abda9Skristaps 	{ termp_rs_pre, NULL }, /* Rs */
208f73abda9Skristaps 	{ NULL, NULL }, /* Sc */
2092c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* So */
2102c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Sq */
211f73abda9Skristaps 	{ termp_sm_pre, NULL }, /* Sm */
212a231c1b4Sschwarze 	{ termp_under_pre, NULL }, /* Sx */
2130ac7e6ecSschwarze 	{ termp_bold_pre, NULL }, /* Sy */
214f73abda9Skristaps 	{ NULL, NULL }, /* Tn */
215816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Ux */
216f73abda9Skristaps 	{ NULL, NULL }, /* Xc */
217f73abda9Skristaps 	{ NULL, NULL }, /* Xo */
218f73abda9Skristaps 	{ termp_fo_pre, termp_fo_post }, /* Fo */
219f73abda9Skristaps 	{ NULL, NULL }, /* Fc */
2202c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Oo */
221f73abda9Skristaps 	{ NULL, NULL }, /* Oc */
2221eead9a6Sschwarze 	{ termp_bk_pre, termp_bk_post }, /* Bk */
223f73abda9Skristaps 	{ NULL, NULL }, /* Ek */
2248ccddcd3Sschwarze 	{ NULL, NULL }, /* Bt */
225f73abda9Skristaps 	{ NULL, NULL }, /* Hf */
226551cd4a8Sschwarze 	{ termp_under_pre, NULL }, /* Fr */
2278ccddcd3Sschwarze 	{ NULL, NULL }, /* Ud */
22871719887Sschwarze 	{ NULL, termp_lb_post }, /* Lb */
2297c539ecbSschwarze 	{ termp_abort_pre, NULL }, /* Lp */
230f73abda9Skristaps 	{ termp_lk_pre, NULL }, /* Lk */
231a231c1b4Sschwarze 	{ termp_under_pre, NULL }, /* Mt */
2322c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Brq */
2332c3450fcSschwarze 	{ termp_quote_pre, termp_quote_post }, /* Bro */
234f73abda9Skristaps 	{ NULL, NULL }, /* Brc */
235011fe33bSschwarze 	{ NULL, termp____post }, /* %C */
23678bbbab4Sschwarze 	{ termp_skip_pre, NULL }, /* Es */
237551cd4a8Sschwarze 	{ termp_quote_pre, termp_quote_post }, /* En */
238816c3c54Sschwarze 	{ termp_xx_pre, termp_xx_post }, /* Dx */
239011fe33bSschwarze 	{ NULL, termp____post }, /* %Q */
2400397c682Sschwarze 	{ NULL, termp____post }, /* %U */
2416093755cSschwarze 	{ NULL, NULL }, /* Ta */
2429a542ed3Sschwarze 	{ termp_skip_pre, NULL }, /* Tg */
243f73abda9Skristaps };
244f73abda9Skristaps 
24514a309e3Sschwarze 
2466ae2e8acSschwarze void
terminal_mdoc(void * arg,const struct roff_meta * mdoc)2476b86842eSschwarze terminal_mdoc(void *arg, const struct roff_meta *mdoc)
248f73abda9Skristaps {
249af29ff23Sschwarze 	struct roff_node	*n, *nn;
2504175bdabSschwarze 	struct termp		*p;
2514175bdabSschwarze 
2524175bdabSschwarze 	p = (struct termp *)arg;
253e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
254f7242c43Sschwarze 	term_tab_set(p, NULL);
255f7242c43Sschwarze 	term_tab_set(p, "T");
256f7242c43Sschwarze 	term_tab_set(p, ".5i");
257ac531cf1Sschwarze 
2586d0e9b63Sschwarze 	n = mdoc->first->child;
2590a0199c7Sschwarze 	if (p->synopsisonly) {
260af29ff23Sschwarze 		for (nn = NULL; n != NULL; n = n->next) {
261af29ff23Sschwarze 			if (n->tok != MDOC_Sh)
262af29ff23Sschwarze 				continue;
263af29ff23Sschwarze 			if (n->sec == SEC_SYNOPSIS)
2640a0199c7Sschwarze 				break;
265af29ff23Sschwarze 			if (nn == NULL && n->sec == SEC_NAME)
266af29ff23Sschwarze 				nn = n;
2672e162c13Sschwarze 		}
268af29ff23Sschwarze 		if (n == NULL)
269af29ff23Sschwarze 			n = nn;
270af29ff23Sschwarze 		p->flags |= TERMP_NOSPACE;
271af29ff23Sschwarze 		if (n != NULL && (n = n->child->next->child) != NULL)
272af29ff23Sschwarze 			print_mdoc_nodelist(p, NULL, mdoc, n);
273af29ff23Sschwarze 		term_newln(p);
2740a0199c7Sschwarze 	} else {
2756b86842eSschwarze 		term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
2764c293873Sschwarze 		while (n != NULL &&
2774c293873Sschwarze 		    (n->type == ROFFT_COMMENT ||
2784c293873Sschwarze 		     n->flags & NODE_NOPRT))
27943808411Sschwarze 			n = n->next;
2800a0199c7Sschwarze 		if (n != NULL) {
2810a0199c7Sschwarze 			if (n->tok != MDOC_Sh)
2820a0199c7Sschwarze 				term_vspace(p);
2836b86842eSschwarze 			print_mdoc_nodelist(p, NULL, mdoc, n);
2840a0199c7Sschwarze 		}
285f95d589eSschwarze 		term_end(p);
286f73abda9Skristaps 	}
2870a0199c7Sschwarze }
288f73abda9Skristaps 
289f73abda9Skristaps static void
print_mdoc_nodelist(DECL_ARGS)290fa70b73eSschwarze print_mdoc_nodelist(DECL_ARGS)
291f73abda9Skristaps {
292e4534905Sschwarze 	while (n != NULL) {
2937ead8a4eSschwarze 		print_mdoc_node(p, pair, meta, n);
294e4534905Sschwarze 		n = n->next;
295e4534905Sschwarze 	}
296f73abda9Skristaps }
297f73abda9Skristaps 
298f73abda9Skristaps static void
print_mdoc_node(DECL_ARGS)299fa70b73eSschwarze print_mdoc_node(DECL_ARGS)
300f73abda9Skristaps {
30116fe0cfcSschwarze 	const struct mdoc_term_act *act;
302f73abda9Skristaps 	struct termpair	 npair;
303c5509750Sschwarze 	size_t		 offset, rmargin;
30416fe0cfcSschwarze 	int		 chld;
305f73abda9Skristaps 
306e1e1437fSschwarze 	/*
307e1e1437fSschwarze 	 * In no-fill mode, break the output line at the beginning
308e1e1437fSschwarze 	 * of new input lines except after \c, and nowhere else.
309e1e1437fSschwarze 	 */
310e1e1437fSschwarze 
311e1e1437fSschwarze 	if (n->flags & NODE_NOFILL) {
312e1e1437fSschwarze 		if (n->flags & NODE_LINE &&
313e1e1437fSschwarze 		    (p->flags & TERMP_NONEWLINE) == 0)
314e1e1437fSschwarze 			term_newln(p);
315e1e1437fSschwarze 		p->flags |= TERMP_BRNEVER;
31618bbf166Sschwarze 	} else {
31718bbf166Sschwarze 		if (n->flags & NODE_LINE)
31818bbf166Sschwarze 			term_tab_ref(p);
319e1e1437fSschwarze 		p->flags &= ~TERMP_BRNEVER;
32018bbf166Sschwarze 	}
321e1e1437fSschwarze 
3224c293873Sschwarze 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
32343808411Sschwarze 		return;
32443808411Sschwarze 
325a231c1b4Sschwarze 	chld = 1;
326e93ea447Sschwarze 	offset = p->tcol->offset;
327e93ea447Sschwarze 	rmargin = p->tcol->rmargin;
328c4b0939cSschwarze 	n->flags &= ~NODE_ENDED;
329f29a1da1Sschwarze 	n->prev_font = p->fonti;
330c5509750Sschwarze 
331a66b65d0Sschwarze 	memset(&npair, 0, sizeof(struct termpair));
332f73abda9Skristaps 	npair.ppair = pair;
333f21ae079Sschwarze 
334e053e0fdSschwarze 	if (n->flags & NODE_ID && n->tok != MDOC_Pp &&
335e053e0fdSschwarze 	    (n->tok != MDOC_It || n->type != ROFFT_BLOCK))
3360ac7e6ecSschwarze 		term_tag_write(n, p->line);
3379a542ed3Sschwarze 
338769ee804Sschwarze 	/*
339769ee804Sschwarze 	 * Keeps only work until the end of a line.  If a keep was
340769ee804Sschwarze 	 * invoked in a prior line, revert it to PREKEEP.
341769ee804Sschwarze 	 */
342769ee804Sschwarze 
343c4b0939cSschwarze 	if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
344769ee804Sschwarze 		p->flags &= ~TERMP_KEEP;
345769ee804Sschwarze 		p->flags |= TERMP_PREKEEP;
346769ee804Sschwarze 	}
347769ee804Sschwarze 
348eb14422aSschwarze 	/*
3499a456f81Sschwarze 	 * After the keep flags have been set up, we may now
3509a456f81Sschwarze 	 * produce output.  Note that some pre-handlers do so.
3519a456f81Sschwarze 	 */
3529a456f81Sschwarze 
3535fff9381Sschwarze 	act = NULL;
3549a456f81Sschwarze 	switch (n->type) {
355d1982c71Sschwarze 	case ROFFT_TEXT:
356e1e1437fSschwarze 		if (n->flags & NODE_LINE) {
357e1e1437fSschwarze 			switch (*n->string) {
358e1e1437fSschwarze 			case '\0':
359e1e1437fSschwarze 				if (p->flags & TERMP_NONEWLINE)
3609a456f81Sschwarze 					term_newln(p);
361e1e1437fSschwarze 				else
362e1e1437fSschwarze 					term_vspace(p);
363e1e1437fSschwarze 				return;
364e1e1437fSschwarze 			case ' ':
365e1e1437fSschwarze 				if ((p->flags & TERMP_NONEWLINE) == 0)
366e1e1437fSschwarze 					term_newln(p);
367e1e1437fSschwarze 				break;
368e1e1437fSschwarze 			default:
369e1e1437fSschwarze 				break;
370e1e1437fSschwarze 			}
371e1e1437fSschwarze 		}
372c4b0939cSschwarze 		if (NODE_DELIMC & n->flags)
373a35fc07aSschwarze 			p->flags |= TERMP_NOSPACE;
3749a456f81Sschwarze 		term_word(p, n->string);
375c4b0939cSschwarze 		if (NODE_DELIMO & n->flags)
376a35fc07aSschwarze 			p->flags |= TERMP_NOSPACE;
3779a456f81Sschwarze 		break;
378d1982c71Sschwarze 	case ROFFT_EQN:
379c4b0939cSschwarze 		if ( ! (n->flags & NODE_LINE))
38096e1823cSschwarze 			p->flags |= TERMP_NOSPACE;
381f8618d99Sschwarze 		term_eqn(p, n->eqn);
382c4b0939cSschwarze 		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
383e2624e2fSschwarze 			p->flags |= TERMP_NOSPACE;
3848d973ab1Sschwarze 		break;
385d1982c71Sschwarze 	case ROFFT_TBL:
386945de12cSschwarze 		if (p->tbl.cols == NULL)
387945de12cSschwarze 			term_newln(p);
3889a456f81Sschwarze 		term_tbl(p, n->span);
3899a456f81Sschwarze 		break;
3909a456f81Sschwarze 	default:
39129478532Sschwarze 		if (n->tok < ROFF_MAX) {
39296a5de47Sschwarze 			roff_term_pre(p, n);
393644b390bSschwarze 			return;
39429478532Sschwarze 		}
39529478532Sschwarze 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
39616fe0cfcSschwarze 		act = mdoc_term_acts + (n->tok - MDOC_Dd);
39716fe0cfcSschwarze 		if (act->pre != NULL &&
39830e5ee06Sschwarze 		    (n->end == ENDBODY_NOT || n->child != NULL))
39916fe0cfcSschwarze 			chld = (*act->pre)(p, &npair, meta, n);
4009a456f81Sschwarze 		break;
4019a456f81Sschwarze 	}
4029a456f81Sschwarze 
4038c089adfSschwarze 	if (chld && n->child)
4047ead8a4eSschwarze 		print_mdoc_nodelist(p, &npair, meta, n->child);
405f73abda9Skristaps 
406d0f586adSschwarze 	term_fontpopq(p,
407ae2efdd8Sschwarze 	    (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
408f73abda9Skristaps 
4092791bd1cSschwarze 	switch (n->type) {
410d1982c71Sschwarze 	case ROFFT_TEXT:
4112791bd1cSschwarze 		break;
412d1982c71Sschwarze 	case ROFFT_TBL:
4132791bd1cSschwarze 		break;
414d1982c71Sschwarze 	case ROFFT_EQN:
4158d973ab1Sschwarze 		break;
4162791bd1cSschwarze 	default:
41716fe0cfcSschwarze 		if (act->post == NULL || n->flags & NODE_ENDED)
4182791bd1cSschwarze 			break;
41916fe0cfcSschwarze 		(void)(*act->post)(p, &npair, meta, n);
420c5509750Sschwarze 
42132dadbacSschwarze 		/*
42232dadbacSschwarze 		 * Explicit end tokens not only call the post
42332dadbacSschwarze 		 * handler, but also tell the respective block
42432dadbacSschwarze 		 * that it must not call the post handler again.
42532dadbacSschwarze 		 */
426769ee804Sschwarze 		if (ENDBODY_NOT != n->end)
427c4b0939cSschwarze 			n->body->flags |= NODE_ENDED;
4282791bd1cSschwarze 		break;
42932dadbacSschwarze 	}
43032dadbacSschwarze 
431c4b0939cSschwarze 	if (NODE_EOS & n->flags)
432bc49dbe1Sschwarze 		p->flags |= TERMP_SENTENCE;
433bc49dbe1Sschwarze 
4348e935ceaSschwarze 	if (n->type != ROFFT_TEXT)
435e93ea447Sschwarze 		p->tcol->offset = offset;
436e93ea447Sschwarze 	p->tcol->rmargin = rmargin;
437f73abda9Skristaps }
438f73abda9Skristaps 
439f73abda9Skristaps static void
print_mdoc_foot(struct termp * p,const struct roff_meta * meta)4402a238f45Sschwarze print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
441f73abda9Skristaps {
44201e00683Sschwarze 	size_t sz;
443f95d589eSschwarze 
444fa70b73eSschwarze 	term_fontrepl(p, TERMFONT_NONE);
445fa70b73eSschwarze 
4466422bc17Sschwarze 	/*
4476422bc17Sschwarze 	 * Output the footer in new-groff style, that is, three columns
4486422bc17Sschwarze 	 * with the middle being the manual date and flanking columns
4496422bc17Sschwarze 	 * being the operating system:
4506422bc17Sschwarze 	 *
4516422bc17Sschwarze 	 * SYSTEM                  DATE                    SYSTEM
4526422bc17Sschwarze 	 */
4536422bc17Sschwarze 
454f73abda9Skristaps 	term_vspace(p);
455f73abda9Skristaps 
456e93ea447Sschwarze 	p->tcol->offset = 0;
45701e00683Sschwarze 	sz = term_strlen(p, meta->date);
458e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin > sz ?
45901e00683Sschwarze 	    (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
460d42cab1cSschwarze 	p->trailspace = 1;
4616422bc17Sschwarze 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
462f73abda9Skristaps 
4637ead8a4eSschwarze 	term_word(p, meta->os);
464f73abda9Skristaps 	term_flushln(p);
465f73abda9Skristaps 
466e93ea447Sschwarze 	p->tcol->offset = p->tcol->rmargin;
46701e00683Sschwarze 	sz = term_strlen(p, meta->os);
468e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
4696b4eabafSschwarze 	p->flags |= TERMP_NOSPACE;
470f73abda9Skristaps 
4717ead8a4eSschwarze 	term_word(p, meta->date);
472f73abda9Skristaps 	term_flushln(p);
473f73abda9Skristaps 
474e93ea447Sschwarze 	p->tcol->offset = p->tcol->rmargin;
475e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin;
476d42cab1cSschwarze 	p->trailspace = 0;
4776422bc17Sschwarze 	p->flags &= ~TERMP_NOBREAK;
4786b4eabafSschwarze 	p->flags |= TERMP_NOSPACE;
4796422bc17Sschwarze 
4807ead8a4eSschwarze 	term_word(p, meta->os);
4816422bc17Sschwarze 	term_flushln(p);
4826422bc17Sschwarze 
483e93ea447Sschwarze 	p->tcol->offset = 0;
484e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin;
4856422bc17Sschwarze 	p->flags = 0;
486f73abda9Skristaps }
487f73abda9Skristaps 
488f73abda9Skristaps static void
print_mdoc_head(struct termp * p,const struct roff_meta * meta)4892a238f45Sschwarze print_mdoc_head(struct termp *p, const struct roff_meta *meta)
490f73abda9Skristaps {
4910b2f1307Sschwarze 	char			*volume, *title;
4920b2f1307Sschwarze 	size_t			 vollen, titlen;
493f95d589eSschwarze 
494f73abda9Skristaps 	/*
495f73abda9Skristaps 	 * The header is strange.  It has three components, which are
496f73abda9Skristaps 	 * really two with the first duplicated.  It goes like this:
497f73abda9Skristaps 	 *
498f73abda9Skristaps 	 * IDENTIFIER              TITLE                   IDENTIFIER
499f73abda9Skristaps 	 *
500f73abda9Skristaps 	 * The IDENTIFIER is NAME(SECTION), which is the command-name
501f73abda9Skristaps 	 * (if given, or "unknown" if not) followed by the manual page
502f73abda9Skristaps 	 * section.  These are given in `Dt'.  The TITLE is a free-form
503f73abda9Skristaps 	 * string depending on the manual volume.  If not specified, it
504f73abda9Skristaps 	 * switches on the manual section.
505f73abda9Skristaps 	 */
506f73abda9Skristaps 
5077ead8a4eSschwarze 	assert(meta->vol);
5080b2f1307Sschwarze 	if (NULL == meta->arch)
5090b2f1307Sschwarze 		volume = mandoc_strdup(meta->vol);
5100b2f1307Sschwarze 	else
5110b2f1307Sschwarze 		mandoc_asprintf(&volume, "%s (%s)",
5120b2f1307Sschwarze 		    meta->vol, meta->arch);
5130b2f1307Sschwarze 	vollen = term_strlen(p, volume);
514f73abda9Skristaps 
5153fdead0cSschwarze 	if (NULL == meta->msec)
5163fdead0cSschwarze 		title = mandoc_strdup(meta->title);
5173fdead0cSschwarze 	else
5183fdead0cSschwarze 		mandoc_asprintf(&title, "%s(%s)",
5193fdead0cSschwarze 		    meta->title, meta->msec);
52004bba6bcSschwarze 	titlen = term_strlen(p, title);
521f73abda9Skristaps 
522f73abda9Skristaps 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
523d42cab1cSschwarze 	p->trailspace = 1;
524e93ea447Sschwarze 	p->tcol->offset = 0;
525e93ea447Sschwarze 	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
5260b2f1307Sschwarze 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
52701e00683Sschwarze 	    vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
528f73abda9Skristaps 
529f73abda9Skristaps 	term_word(p, title);
530f73abda9Skristaps 	term_flushln(p);
531f73abda9Skristaps 
5326b4eabafSschwarze 	p->flags |= TERMP_NOSPACE;
533e93ea447Sschwarze 	p->tcol->offset = p->tcol->rmargin;
534e93ea447Sschwarze 	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
535e93ea447Sschwarze 	    p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
536f73abda9Skristaps 
5370b2f1307Sschwarze 	term_word(p, volume);
538f73abda9Skristaps 	term_flushln(p);
539f73abda9Skristaps 
54004bba6bcSschwarze 	p->flags &= ~TERMP_NOBREAK;
541d42cab1cSschwarze 	p->trailspace = 0;
542e93ea447Sschwarze 	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
54304bba6bcSschwarze 		p->flags |= TERMP_NOSPACE;
544e93ea447Sschwarze 		p->tcol->offset = p->tcol->rmargin;
545e93ea447Sschwarze 		p->tcol->rmargin = p->maxrmargin;
546f73abda9Skristaps 		term_word(p, title);
547f73abda9Skristaps 		term_flushln(p);
54804bba6bcSschwarze 	}
549f73abda9Skristaps 
55004bba6bcSschwarze 	p->flags &= ~TERMP_NOSPACE;
551e93ea447Sschwarze 	p->tcol->offset = 0;
552e93ea447Sschwarze 	p->tcol->rmargin = p->maxrmargin;
553a92c1cd8Sschwarze 	free(title);
5540b2f1307Sschwarze 	free(volume);
555f73abda9Skristaps }
556f73abda9Skristaps 
55725da2733Sschwarze static int
a2width(const struct termp * p,const char * v)5583ebeb861Sschwarze a2width(const struct termp *p, const char *v)
5594175bdabSschwarze {
5604175bdabSschwarze 	struct roffsu	 su;
561ecd22486Sschwarze 	const char	*end;
5624175bdabSschwarze 
563ecd22486Sschwarze 	end = a2roffsu(v, &su, SCALE_MAX);
564ecd22486Sschwarze 	if (end == NULL || *end != '\0') {
565f463ffc6Sschwarze 		su.unit = SCALE_EN;
566f463ffc6Sschwarze 		su.scale = term_strlen(p, v) / term_strlen(p, "0");
56725da2733Sschwarze 	}
568f4692b45Sschwarze 	return term_hen(p, &su);
5694175bdabSschwarze }
5704175bdabSschwarze 
5715d7d1836Sschwarze /*
5725d7d1836Sschwarze  * Determine how much space to print out before block elements of `It'
5735d7d1836Sschwarze  * (and thus `Bl') and `Bd'.  And then go ahead and print that space,
5745d7d1836Sschwarze  * too.
5755d7d1836Sschwarze  */
576152a4d05Sschwarze static void
print_bvspace(struct termp * p,struct roff_node * bl,struct roff_node * n)5777ebbefbeSschwarze print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n)
578f73abda9Skristaps {
5797ebbefbeSschwarze 	struct roff_node *nn;
5801cdbf331Sschwarze 
581f73abda9Skristaps 	term_newln(p);
58231e23753Sschwarze 
5837ebbefbeSschwarze 	if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) ||
5847ebbefbeSschwarze 	    (bl->tok == MDOC_Bl && bl->norm->Bl.comp))
585152a4d05Sschwarze 		return;
586152a4d05Sschwarze 
5878c089adfSschwarze 	/* Do not vspace directly after Ss/Sh. */
588f73abda9Skristaps 
589831618f8Sschwarze 	nn = n;
5907ebbefbeSschwarze 	while (roff_node_prev(nn) == NULL) {
591831618f8Sschwarze 		do {
592831618f8Sschwarze 			nn = nn->parent;
593d1982c71Sschwarze 			if (nn->type == ROFFT_ROOT)
594152a4d05Sschwarze 				return;
595d1982c71Sschwarze 		} while (nn->type != ROFFT_BLOCK);
596831618f8Sschwarze 		if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
597152a4d05Sschwarze 			return;
598831618f8Sschwarze 		if (nn->tok == MDOC_It &&
599831618f8Sschwarze 		    nn->parent->parent->norm->Bl.type != LIST_item)
600f73abda9Skristaps 			break;
601f73abda9Skristaps 	}
602f73abda9Skristaps 
6037ebbefbeSschwarze 	/*
6047ebbefbeSschwarze 	 * No vertical space after:
6057ebbefbeSschwarze 	 * items in .Bl -column
6067ebbefbeSschwarze 	 * items without a body in .Bl -diag
6077ebbefbeSschwarze 	 */
608152a4d05Sschwarze 
6097ebbefbeSschwarze 	if (bl->tok != MDOC_Bl ||
6107ebbefbeSschwarze 	    n->prev == NULL || n->prev->tok != MDOC_It ||
6117ebbefbeSschwarze 	    (bl->norm->Bl.type != LIST_column &&
6127ebbefbeSschwarze 	     (bl->norm->Bl.type != LIST_diag ||
6137ebbefbeSschwarze 	      n->prev->body->child != NULL)))
614152a4d05Sschwarze 		term_vspace(p);
615f73abda9Skristaps }
616f73abda9Skristaps 
617f73abda9Skristaps 
618f73abda9Skristaps static int
termp_it_pre(DECL_ARGS)619f73abda9Skristaps termp_it_pre(DECL_ARGS)
620f73abda9Skristaps {
621d43cc8c2Sschwarze 	struct roffsu		su;
62247813146Sschwarze 	char			buf[24];
6233a0d07afSschwarze 	const struct roff_node *bl, *nn;
62425da2733Sschwarze 	size_t			ncols, dcol;
62525da2733Sschwarze 	int			i, offset, width;
62650e63e03Sschwarze 	enum mdoc_list		type;
627f73abda9Skristaps 
628d1982c71Sschwarze 	if (n->type == ROFFT_BLOCK) {
6294175bdabSschwarze 		print_bvspace(p, n->parent->parent, n);
630e053e0fdSschwarze 		if (n->flags & NODE_ID)
631e053e0fdSschwarze 			term_tag_write(n, p->line);
632526e306bSschwarze 		return 1;
633152a4d05Sschwarze 	}
634f73abda9Skristaps 
6358c089adfSschwarze 	bl = n->parent->parent->parent;
6368c62fbf5Sschwarze 	type = bl->norm->Bl.type;
637f73abda9Skristaps 
6385d7d1836Sschwarze 	/*
63925da2733Sschwarze 	 * Defaults for specific list types.
64025da2733Sschwarze 	 */
64125da2733Sschwarze 
64225da2733Sschwarze 	switch (type) {
64325da2733Sschwarze 	case LIST_bullet:
64425da2733Sschwarze 	case LIST_dash:
64525da2733Sschwarze 	case LIST_hyphen:
64625da2733Sschwarze 	case LIST_enum:
64725da2733Sschwarze 		width = term_len(p, 2);
64825da2733Sschwarze 		break;
64925da2733Sschwarze 	case LIST_hang:
6500ed87641Sschwarze 	case LIST_tag:
65125da2733Sschwarze 		width = term_len(p, 8);
65225da2733Sschwarze 		break;
65325da2733Sschwarze 	case LIST_column:
65425da2733Sschwarze 		width = term_len(p, 10);
65525da2733Sschwarze 		break;
65625da2733Sschwarze 	default:
65725da2733Sschwarze 		width = 0;
65825da2733Sschwarze 		break;
65925da2733Sschwarze 	}
66025da2733Sschwarze 	offset = 0;
66125da2733Sschwarze 
66225da2733Sschwarze 	/*
6635d7d1836Sschwarze 	 * First calculate width and offset.  This is pretty easy unless
6645d7d1836Sschwarze 	 * we're a -column list, in which case all prior columns must
6655d7d1836Sschwarze 	 * be accounted for.
6665d7d1836Sschwarze 	 */
6675d7d1836Sschwarze 
66825da2733Sschwarze 	if (bl->norm->Bl.offs != NULL) {
66990d52a15Sschwarze 		offset = a2width(p, bl->norm->Bl.offs);
670e93ea447Sschwarze 		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
671e93ea447Sschwarze 			offset = -p->tcol->offset;
67225da2733Sschwarze 		else if (offset > SHRT_MAX)
67325da2733Sschwarze 			offset = 0;
67425da2733Sschwarze 	}
6750ce9233aSschwarze 
676f73abda9Skristaps 	switch (type) {
67749aff9f8Sschwarze 	case LIST_column:
678d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
679f73abda9Skristaps 			break;
68031e23753Sschwarze 
6813189548fSschwarze 		/*
6825d7d1836Sschwarze 		 * Imitate groff's column handling:
6835d7d1836Sschwarze 		 * - For each earlier column, add its width.
6845d7d1836Sschwarze 		 * - For less than 5 columns, add four more blanks per
6855d7d1836Sschwarze 		 *   column.
6865d7d1836Sschwarze 		 * - For exactly 5 columns, add three more blank per
6875d7d1836Sschwarze 		 *   column.
6885d7d1836Sschwarze 		 * - For more than 5 columns, add only one column.
6893189548fSschwarze 		 */
6908c62fbf5Sschwarze 		ncols = bl->norm->Bl.ncols;
6913ebeb861Sschwarze 		dcol = ncols < 5 ? term_len(p, 4) :
6923ebeb861Sschwarze 		    ncols == 5 ? term_len(p, 3) : term_len(p, 1);
6935d7d1836Sschwarze 
6946093755cSschwarze 		/*
695d1982c71Sschwarze 		 * Calculate the offset by applying all prior ROFFT_BODY,
696d1982c71Sschwarze 		 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
6976093755cSschwarze 		 */
6986093755cSschwarze 
6995d7d1836Sschwarze 		for (i = 0, nn = n->prev;
7006093755cSschwarze 		    nn->prev && i < (int)ncols;
701d43cc8c2Sschwarze 		    nn = nn->prev, i++) {
702f463ffc6Sschwarze 			su.unit = SCALE_EN;
703f463ffc6Sschwarze 			su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
704f463ffc6Sschwarze 			    term_strlen(p, "0");
705f4692b45Sschwarze 			offset += term_hen(p, &su) + dcol;
706d43cc8c2Sschwarze 		}
7073189548fSschwarze 
7080ce9233aSschwarze 		/*
7095d7d1836Sschwarze 		 * When exceeding the declared number of columns, leave
7105d7d1836Sschwarze 		 * the remaining widths at 0.  This will later be
7115d7d1836Sschwarze 		 * adjusted to the default width of 10, or, for the last
7125d7d1836Sschwarze 		 * column, stretched to the right margin.
7130ce9233aSschwarze 		 */
7145d7d1836Sschwarze 		if (i >= (int)ncols)
7155d7d1836Sschwarze 			break;
7163189548fSschwarze 
7170ce9233aSschwarze 		/*
7185d7d1836Sschwarze 		 * Use the declared column widths, extended as explained
7195d7d1836Sschwarze 		 * in the preceding paragraph.
7200ce9233aSschwarze 		 */
721f463ffc6Sschwarze 		su.unit = SCALE_EN;
722f463ffc6Sschwarze 		su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
723f463ffc6Sschwarze 		    term_strlen(p, "0");
724f4692b45Sschwarze 		width = term_hen(p, &su) + dcol;
725f73abda9Skristaps 		break;
726f73abda9Skristaps 	default:
7278c62fbf5Sschwarze 		if (NULL == bl->norm->Bl.width)
7285d7d1836Sschwarze 			break;
7295d7d1836Sschwarze 
7305d7d1836Sschwarze 		/*
7315d7d1836Sschwarze 		 * Note: buffer the width by 2, which is groff's magic
7325d7d1836Sschwarze 		 * number for buffering single arguments.  See the above
7335d7d1836Sschwarze 		 * handling for column for how this changes.
7345d7d1836Sschwarze 		 */
7358c62fbf5Sschwarze 		width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
736e93ea447Sschwarze 		if (width < 0 && (size_t)(-width) > p->tcol->offset)
737e93ea447Sschwarze 			width = -p->tcol->offset;
73825da2733Sschwarze 		else if (width > SHRT_MAX)
73925da2733Sschwarze 			width = 0;
740f73abda9Skristaps 		break;
741f73abda9Skristaps 	}
742f73abda9Skristaps 
743f73abda9Skristaps 	/*
744ba61e67eSschwarze 	 * Whitespace control.  Inset bodies need an initial space,
745ba61e67eSschwarze 	 * while diagonal bodies need two.
746f73abda9Skristaps 	 */
747f73abda9Skristaps 
748bb89415cSschwarze 	p->flags |= TERMP_NOSPACE;
749bb89415cSschwarze 
750f73abda9Skristaps 	switch (type) {
75149aff9f8Sschwarze 	case LIST_diag:
752d1982c71Sschwarze 		if (n->type == ROFFT_BODY)
753bb89415cSschwarze 			term_word(p, "\\ \\ ");
754bb89415cSschwarze 		break;
75549aff9f8Sschwarze 	case LIST_inset:
75630e5ee06Sschwarze 		if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
757bb89415cSschwarze 			term_word(p, "\\ ");
758f73abda9Skristaps 		break;
759f73abda9Skristaps 	default:
760f73abda9Skristaps 		break;
761f73abda9Skristaps 	}
762f73abda9Skristaps 
763bb89415cSschwarze 	p->flags |= TERMP_NOSPACE;
764bb89415cSschwarze 
765f73abda9Skristaps 	switch (type) {
76649aff9f8Sschwarze 	case LIST_diag:
767d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
768fa70b73eSschwarze 			term_fontpush(p, TERMFONT_BOLD);
769f73abda9Skristaps 		break;
770f73abda9Skristaps 	default:
771f73abda9Skristaps 		break;
772f73abda9Skristaps 	}
773f73abda9Skristaps 
774f73abda9Skristaps 	/*
7755d7d1836Sschwarze 	 * Pad and break control.  This is the tricky part.  These flags
7765d7d1836Sschwarze 	 * are documented in term_flushln() in term.c.  Note that we're
7775d7d1836Sschwarze 	 * going to unset all of these flags in termp_it_post() when we
7785d7d1836Sschwarze 	 * exit.
779f73abda9Skristaps 	 */
780f73abda9Skristaps 
781f73abda9Skristaps 	switch (type) {
78249aff9f8Sschwarze 	case LIST_enum:
7839630d74cSschwarze 	case LIST_bullet:
7849630d74cSschwarze 	case LIST_dash:
7859630d74cSschwarze 	case LIST_hyphen:
786771c54bcSschwarze 		if (n->type == ROFFT_HEAD) {
787771c54bcSschwarze 			p->flags |= TERMP_NOBREAK | TERMP_HANG;
788d42cab1cSschwarze 			p->trailspace = 1;
789771c54bcSschwarze 		} else if (width <= (int)term_len(p, 2))
790771c54bcSschwarze 			p->flags |= TERMP_NOPAD;
791e9723699Sschwarze 		break;
79249aff9f8Sschwarze 	case LIST_hang:
793d1982c71Sschwarze 		if (n->type != ROFFT_HEAD)
7947039ffb7Sschwarze 			break;
795eeb5fd14Sschwarze 		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
796d42cab1cSschwarze 		p->trailspace = 1;
797e9723699Sschwarze 		break;
79849aff9f8Sschwarze 	case LIST_tag:
799d1982c71Sschwarze 		if (n->type != ROFFT_HEAD)
800e9723699Sschwarze 			break;
801d42cab1cSschwarze 
802b8d4b727Sschwarze 		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
803d42cab1cSschwarze 		p->trailspace = 2;
804d42cab1cSschwarze 
8058c089adfSschwarze 		if (NULL == n->next || NULL == n->next->child)
806771c54bcSschwarze 			p->flags |= TERMP_HANG;
807f73abda9Skristaps 		break;
80849aff9f8Sschwarze 	case LIST_column:
809d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
8106093755cSschwarze 			break;
8116093755cSschwarze 
812d42cab1cSschwarze 		if (NULL == n->next) {
813f73abda9Skristaps 			p->flags &= ~TERMP_NOBREAK;
814d42cab1cSschwarze 			p->trailspace = 0;
815d42cab1cSschwarze 		} else {
816f73abda9Skristaps 			p->flags |= TERMP_NOBREAK;
817d42cab1cSschwarze 			p->trailspace = 1;
818d42cab1cSschwarze 		}
8196093755cSschwarze 
820f73abda9Skristaps 		break;
82149aff9f8Sschwarze 	case LIST_diag:
822d1982c71Sschwarze 		if (n->type != ROFFT_HEAD)
823d42cab1cSschwarze 			break;
824eeb5fd14Sschwarze 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
825d42cab1cSschwarze 		p->trailspace = 1;
826f73abda9Skristaps 		break;
827f73abda9Skristaps 	default:
828f73abda9Skristaps 		break;
829f73abda9Skristaps 	}
830f73abda9Skristaps 
831f73abda9Skristaps 	/*
832f73abda9Skristaps 	 * Margin control.  Set-head-width lists have their right
833f73abda9Skristaps 	 * margins shortened.  The body for these lists has the offset
834f73abda9Skristaps 	 * necessarily lengthened.  Everybody gets the offset.
835f73abda9Skristaps 	 */
836f73abda9Skristaps 
837e93ea447Sschwarze 	p->tcol->offset += offset;
838f73abda9Skristaps 
839f73abda9Skristaps 	switch (type) {
84049aff9f8Sschwarze 	case LIST_bullet:
84149aff9f8Sschwarze 	case LIST_dash:
84249aff9f8Sschwarze 	case LIST_enum:
84349aff9f8Sschwarze 	case LIST_hyphen:
844771c54bcSschwarze 	case LIST_hang:
84549aff9f8Sschwarze 	case LIST_tag:
846d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
847e93ea447Sschwarze 			p->tcol->rmargin = p->tcol->offset + width;
84801e00683Sschwarze 		else
849e93ea447Sschwarze 			p->tcol->offset += width;
850f73abda9Skristaps 		break;
85149aff9f8Sschwarze 	case LIST_column:
85253292e81Sschwarze 		assert(width);
853e93ea447Sschwarze 		p->tcol->rmargin = p->tcol->offset + width;
8548ae554d6Sschwarze 		/*
8558ae554d6Sschwarze 		 * XXX - this behaviour is not documented: the
8568ae554d6Sschwarze 		 * right-most column is filled to the right margin.
8578ae554d6Sschwarze 		 */
858d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
8596093755cSschwarze 			break;
860e93ea447Sschwarze 		if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
861e93ea447Sschwarze 			p->tcol->rmargin = p->maxrmargin;
862f73abda9Skristaps 		break;
863f73abda9Skristaps 	default:
864f73abda9Skristaps 		break;
865f73abda9Skristaps 	}
866f73abda9Skristaps 
867f73abda9Skristaps 	/*
868f73abda9Skristaps 	 * The dash, hyphen, bullet and enum lists all have a special
8694c45d6c2Sschwarze 	 * HEAD character (temporarily bold, in some cases).
870f73abda9Skristaps 	 */
871f73abda9Skristaps 
872d1982c71Sschwarze 	if (n->type == ROFFT_HEAD)
873f73abda9Skristaps 		switch (type) {
87449aff9f8Sschwarze 		case LIST_bullet:
875fa70b73eSschwarze 			term_fontpush(p, TERMFONT_BOLD);
876f73abda9Skristaps 			term_word(p, "\\[bu]");
877fa70b73eSschwarze 			term_fontpop(p);
878f73abda9Skristaps 			break;
87949aff9f8Sschwarze 		case LIST_dash:
88049aff9f8Sschwarze 		case LIST_hyphen:
881fa70b73eSschwarze 			term_fontpush(p, TERMFONT_BOLD);
882ad68a70fSschwarze 			term_word(p, "-");
883fa70b73eSschwarze 			term_fontpop(p);
884f73abda9Skristaps 			break;
88549aff9f8Sschwarze 		case LIST_enum:
886f73abda9Skristaps 			(pair->ppair->ppair->count)++;
88747813146Sschwarze 			(void)snprintf(buf, sizeof(buf), "%d.",
888f73abda9Skristaps 			    pair->ppair->ppair->count);
889f73abda9Skristaps 			term_word(p, buf);
890f73abda9Skristaps 			break;
891f73abda9Skristaps 		default:
892f73abda9Skristaps 			break;
893f73abda9Skristaps 		}
894f73abda9Skristaps 
895f73abda9Skristaps 	/*
896f73abda9Skristaps 	 * If we're not going to process our children, indicate so here.
897f73abda9Skristaps 	 */
898f73abda9Skristaps 
899f73abda9Skristaps 	switch (type) {
90049aff9f8Sschwarze 	case LIST_bullet:
90149aff9f8Sschwarze 	case LIST_item:
90249aff9f8Sschwarze 	case LIST_dash:
90349aff9f8Sschwarze 	case LIST_hyphen:
90449aff9f8Sschwarze 	case LIST_enum:
905d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
906526e306bSschwarze 			return 0;
907f73abda9Skristaps 		break;
90849aff9f8Sschwarze 	case LIST_column:
909d1982c71Sschwarze 		if (n->type == ROFFT_HEAD)
910526e306bSschwarze 			return 0;
911771c54bcSschwarze 		p->minbl = 0;
912f73abda9Skristaps 		break;
913f73abda9Skristaps 	default:
914f73abda9Skristaps 		break;
915f73abda9Skristaps 	}
916f73abda9Skristaps 
917526e306bSschwarze 	return 1;
918f73abda9Skristaps }
919f73abda9Skristaps 
920f73abda9Skristaps static void
termp_it_post(DECL_ARGS)921f73abda9Skristaps termp_it_post(DECL_ARGS)
922f73abda9Skristaps {
92350e63e03Sschwarze 	enum mdoc_list	   type;
924f73abda9Skristaps 
925d1982c71Sschwarze 	if (n->type == ROFFT_BLOCK)
926f73abda9Skristaps 		return;
927f73abda9Skristaps 
9288c62fbf5Sschwarze 	type = n->parent->parent->parent->norm->Bl.type;
929f73abda9Skristaps 
930f73abda9Skristaps 	switch (type) {
93149aff9f8Sschwarze 	case LIST_item:
93249aff9f8Sschwarze 	case LIST_diag:
93349aff9f8Sschwarze 	case LIST_inset:
934d1982c71Sschwarze 		if (n->type == ROFFT_BODY)
935a1dd05fdSschwarze 			term_newln(p);
936f73abda9Skristaps 		break;
93749aff9f8Sschwarze 	case LIST_column:
938d1982c71Sschwarze 		if (n->type == ROFFT_BODY)
939f73abda9Skristaps 			term_flushln(p);
940f73abda9Skristaps 		break;
941f73abda9Skristaps 	default:
942a1dd05fdSschwarze 		term_newln(p);
943f73abda9Skristaps 		break;
944f73abda9Skristaps 	}
945f73abda9Skristaps 
9465d7d1836Sschwarze 	/*
9475d7d1836Sschwarze 	 * Now that our output is flushed, we can reset our tags.  Since
9485d7d1836Sschwarze 	 * only `It' sets these flags, we're free to assume that nobody
9495d7d1836Sschwarze 	 * has munged them in the meanwhile.
9505d7d1836Sschwarze 	 */
9515d7d1836Sschwarze 
952771c54bcSschwarze 	p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
953d42cab1cSschwarze 	p->trailspace = 0;
954f73abda9Skristaps }
955f73abda9Skristaps 
956f73abda9Skristaps static int
termp_nm_pre(DECL_ARGS)957f73abda9Skristaps termp_nm_pre(DECL_ARGS)
958f73abda9Skristaps {
959a19c2534Sschwarze 	const char	*cp;
960f73abda9Skristaps 
961d1982c71Sschwarze 	if (n->type == ROFFT_BLOCK) {
9624b1aedf8Sschwarze 		p->flags |= TERMP_PREKEEP;
963526e306bSschwarze 		return 1;
9644b1aedf8Sschwarze 	}
9656e03d529Sschwarze 
966d1982c71Sschwarze 	if (n->type == ROFFT_BODY) {
967e93ea447Sschwarze 		if (n->child == NULL)
968526e306bSschwarze 			return 0;
9696b4eabafSschwarze 		p->flags |= TERMP_NOSPACE;
970a19c2534Sschwarze 		cp = NULL;
971a19c2534Sschwarze 		if (n->prev->child != NULL)
972a19c2534Sschwarze 		    cp = n->prev->child->string;
973a19c2534Sschwarze 		if (cp == NULL)
974a19c2534Sschwarze 			cp = meta->name;
975a19c2534Sschwarze 		if (cp == NULL)
976e93ea447Sschwarze 			p->tcol->offset += term_len(p, 6);
977a19c2534Sschwarze 		else
978e93ea447Sschwarze 			p->tcol->offset += term_len(p, 1) +
979e93ea447Sschwarze 			    term_strlen(p, cp);
980526e306bSschwarze 		return 1;
9812ab36204Sschwarze 	}
9822ab36204Sschwarze 
98390957cf5Sschwarze 	if (n->child == NULL)
984526e306bSschwarze 		return 0;
9852ab36204Sschwarze 
986d1982c71Sschwarze 	if (n->type == ROFFT_HEAD)
9872fa243a3Sschwarze 		synopsis_pre(p, n->parent);
988fa70b73eSschwarze 
989d1982c71Sschwarze 	if (n->type == ROFFT_HEAD &&
990e93ea447Sschwarze 	    n->next != NULL && n->next->child != NULL) {
991eeb5fd14Sschwarze 		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
992d42cab1cSschwarze 		p->trailspace = 1;
993e93ea447Sschwarze 		p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
994e93ea447Sschwarze 		if (n->child == NULL)
995e93ea447Sschwarze 			p->tcol->rmargin += term_strlen(p, meta->name);
996e93ea447Sschwarze 		else if (n->child->type == ROFFT_TEXT) {
997e93ea447Sschwarze 			p->tcol->rmargin += term_strlen(p, n->child->string);
998e93ea447Sschwarze 			if (n->child->next != NULL)
9999e3c1948Sschwarze 				p->flags |= TERMP_HANG;
10009e3c1948Sschwarze 		} else {
1001e93ea447Sschwarze 			p->tcol->rmargin += term_len(p, 5);
10029e3c1948Sschwarze 			p->flags |= TERMP_HANG;
10039e3c1948Sschwarze 		}
10042ab36204Sschwarze 	}
10050ac7e6ecSschwarze 	return termp_bold_pre(p, pair, meta, n);
1006f73abda9Skristaps }
1007f73abda9Skristaps 
10082ab36204Sschwarze static void
termp_nm_post(DECL_ARGS)10092ab36204Sschwarze termp_nm_post(DECL_ARGS)
10102ab36204Sschwarze {
10110ac7e6ecSschwarze 	switch (n->type) {
10120ac7e6ecSschwarze 	case ROFFT_BLOCK:
10134b1aedf8Sschwarze 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
10140ac7e6ecSschwarze 		break;
10150ac7e6ecSschwarze 	case ROFFT_HEAD:
10160ac7e6ecSschwarze 		if (n->next == NULL || n->next->child == NULL)
10170ac7e6ecSschwarze 			break;
10182ab36204Sschwarze 		term_flushln(p);
1019eeb5fd14Sschwarze 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1020d42cab1cSschwarze 		p->trailspace = 0;
10210ac7e6ecSschwarze 		break;
10220ac7e6ecSschwarze 	case ROFFT_BODY:
10230ac7e6ecSschwarze 		if (n->child != NULL)
10242ab36204Sschwarze 			term_flushln(p);
10250ac7e6ecSschwarze 		break;
10260ac7e6ecSschwarze 	default:
10270ac7e6ecSschwarze 		break;
10280ac7e6ecSschwarze 	}
10292ab36204Sschwarze }
10302ab36204Sschwarze 
1031f73abda9Skristaps static int
termp_fl_pre(DECL_ARGS)1032f73abda9Skristaps termp_fl_pre(DECL_ARGS)
1033f73abda9Skristaps {
10347ebbefbeSschwarze 	struct roff_node *nn;
1035f73abda9Skristaps 
1036fa70b73eSschwarze 	term_fontpush(p, TERMFONT_BOLD);
1037f73abda9Skristaps 	term_word(p, "\\-");
1038e974da76Sschwarze 
10397ebbefbeSschwarze 	if (n->child != NULL ||
10407ebbefbeSschwarze 	    ((nn = roff_node_next(n)) != NULL &&
10417ebbefbeSschwarze 	     nn->type != ROFFT_TEXT &&
10427ebbefbeSschwarze 	     (nn->flags & NODE_LINE) == 0))
1043b16e7ddfSschwarze 		p->flags |= TERMP_NOSPACE;
1044e974da76Sschwarze 
1045526e306bSschwarze 	return 1;
1046f73abda9Skristaps }
1047f73abda9Skristaps 
1048f73abda9Skristaps static int
termp__a_pre(DECL_ARGS)1049bf782e42Sschwarze termp__a_pre(DECL_ARGS)
1050bf782e42Sschwarze {
10517ebbefbeSschwarze 	struct roff_node *nn;
1052bf782e42Sschwarze 
10537ebbefbeSschwarze 	if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A &&
10547ebbefbeSschwarze 	    ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A))
1055bf782e42Sschwarze 		term_word(p, "and");
1056bf782e42Sschwarze 
1057526e306bSschwarze 	return 1;
1058bf782e42Sschwarze }
1059bf782e42Sschwarze 
1060bf782e42Sschwarze static int
termp_an_pre(DECL_ARGS)10618f46e13cSschwarze termp_an_pre(DECL_ARGS)
10628f46e13cSschwarze {
10638f46e13cSschwarze 
10643eeea6b8Sschwarze 	if (n->norm->An.auth == AUTH_split) {
10658f46e13cSschwarze 		p->flags &= ~TERMP_NOSPLIT;
10668f46e13cSschwarze 		p->flags |= TERMP_SPLIT;
1067526e306bSschwarze 		return 0;
10683eeea6b8Sschwarze 	}
10693eeea6b8Sschwarze 	if (n->norm->An.auth == AUTH_nosplit) {
10708f46e13cSschwarze 		p->flags &= ~TERMP_SPLIT;
10718f46e13cSschwarze 		p->flags |= TERMP_NOSPLIT;
1072526e306bSschwarze 		return 0;
10738f46e13cSschwarze 	}
10748f46e13cSschwarze 
10753eeea6b8Sschwarze 	if (p->flags & TERMP_SPLIT)
10763eeea6b8Sschwarze 		term_newln(p);
10773eeea6b8Sschwarze 
10783eeea6b8Sschwarze 	if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
10793eeea6b8Sschwarze 		p->flags |= TERMP_SPLIT;
10803eeea6b8Sschwarze 
1081526e306bSschwarze 	return 1;
10828f46e13cSschwarze }
10838f46e13cSschwarze 
10848f46e13cSschwarze static int
termp_ns_pre(DECL_ARGS)1085f73abda9Skristaps termp_ns_pre(DECL_ARGS)
1086f73abda9Skristaps {
1087f73abda9Skristaps 
1088c4b0939cSschwarze 	if ( ! (NODE_LINE & n->flags))
1089f73abda9Skristaps 		p->flags |= TERMP_NOSPACE;
1090526e306bSschwarze 	return 1;
1091f73abda9Skristaps }
1092f73abda9Skristaps 
1093f73abda9Skristaps static int
termp_rs_pre(DECL_ARGS)1094f73abda9Skristaps termp_rs_pre(DECL_ARGS)
1095f73abda9Skristaps {
10968c089adfSschwarze 	if (SEC_SEE_ALSO != n->sec)
1097526e306bSschwarze 		return 1;
10987ebbefbeSschwarze 	if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL)
1099f73abda9Skristaps 		term_vspace(p);
1100526e306bSschwarze 	return 1;
1101f73abda9Skristaps }
1102f73abda9Skristaps 
1103f73abda9Skristaps static int
termp_ex_pre(DECL_ARGS)1104f73abda9Skristaps termp_ex_pre(DECL_ARGS)
1105f73abda9Skristaps {
1106a35fc07aSschwarze 	term_newln(p);
11078ccddcd3Sschwarze 	return 1;
1108f73abda9Skristaps }
1109f73abda9Skristaps 
1110f73abda9Skristaps static int
termp_nd_pre(DECL_ARGS)1111f73abda9Skristaps termp_nd_pre(DECL_ARGS)
1112f73abda9Skristaps {
1113d1982c71Sschwarze 	if (n->type == ROFFT_BODY)
11144602e85cSschwarze 		term_word(p, "\\(en");
1115526e306bSschwarze 	return 1;
1116f73abda9Skristaps }
1117f73abda9Skristaps 
1118b16e7ddfSschwarze static int
termp_bl_pre(DECL_ARGS)1119b16e7ddfSschwarze termp_bl_pre(DECL_ARGS)
1120b16e7ddfSschwarze {
1121e053e0fdSschwarze 	switch (n->type) {
1122e053e0fdSschwarze 	case ROFFT_BLOCK:
1123e053e0fdSschwarze 		term_newln(p);
1124e053e0fdSschwarze 		return 1;
1125e053e0fdSschwarze 	case ROFFT_HEAD:
1126e053e0fdSschwarze 		return 0;
1127e053e0fdSschwarze 	default:
1128e053e0fdSschwarze 		return 1;
1129e053e0fdSschwarze 	}
1130b16e7ddfSschwarze }
1131b16e7ddfSschwarze 
1132f73abda9Skristaps static void
termp_bl_post(DECL_ARGS)1133f73abda9Skristaps termp_bl_post(DECL_ARGS)
1134f73abda9Skristaps {
1135f7242c43Sschwarze 	if (n->type != ROFFT_BLOCK)
1136f7242c43Sschwarze 		return;
1137f73abda9Skristaps 	term_newln(p);
1138f7242c43Sschwarze 	if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
1139f7242c43Sschwarze 		return;
1140f7242c43Sschwarze 	term_tab_set(p, NULL);
1141f7242c43Sschwarze 	term_tab_set(p, "T");
1142f7242c43Sschwarze 	term_tab_set(p, ".5i");
1143f73abda9Skristaps }
1144f73abda9Skristaps 
1145f73abda9Skristaps static int
termp_xr_pre(DECL_ARGS)1146f73abda9Skristaps termp_xr_pre(DECL_ARGS)
1147f73abda9Skristaps {
1148a35fc07aSschwarze 	if (NULL == (n = n->child))
1149526e306bSschwarze 		return 0;
1150b4dec12aSschwarze 
1151d1982c71Sschwarze 	assert(n->type == ROFFT_TEXT);
1152a35fc07aSschwarze 	term_word(p, n->string);
11538248aaccSschwarze 
1154a35fc07aSschwarze 	if (NULL == (n = n->next))
1155526e306bSschwarze 		return 0;
1156a35fc07aSschwarze 
1157f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1158f73abda9Skristaps 	term_word(p, "(");
1159a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
1160a35fc07aSschwarze 
1161d1982c71Sschwarze 	assert(n->type == ROFFT_TEXT);
1162a35fc07aSschwarze 	term_word(p, n->string);
1163a35fc07aSschwarze 
1164a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
1165f73abda9Skristaps 	term_word(p, ")");
11668c089adfSschwarze 
1167526e306bSschwarze 	return 0;
1168f73abda9Skristaps }
1169f73abda9Skristaps 
11709a98b8a1Sschwarze /*
11719a98b8a1Sschwarze  * This decides how to assert whitespace before any of the SYNOPSIS set
11729a98b8a1Sschwarze  * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
11739a98b8a1Sschwarze  * macro combos).
11749a98b8a1Sschwarze  */
11759a98b8a1Sschwarze static void
synopsis_pre(struct termp * p,struct roff_node * n)11767ebbefbeSschwarze synopsis_pre(struct termp *p, struct roff_node *n)
11779a98b8a1Sschwarze {
11787ebbefbeSschwarze 	struct roff_node	*np;
11797ebbefbeSschwarze 
11807ebbefbeSschwarze 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
11817ebbefbeSschwarze 	    (np = roff_node_prev(n)) == NULL)
11829a98b8a1Sschwarze 		return;
11839a98b8a1Sschwarze 
11849a98b8a1Sschwarze 	/*
11859a98b8a1Sschwarze 	 * If we're the second in a pair of like elements, emit our
11869a98b8a1Sschwarze 	 * newline and return.  UNLESS we're `Fo', `Fn', `Fn', in which
11879a98b8a1Sschwarze 	 * case we soldier on.
11889a98b8a1Sschwarze 	 */
11897ebbefbeSschwarze 	if (np->tok == n->tok &&
11909a98b8a1Sschwarze 	    MDOC_Ft != n->tok &&
11919a98b8a1Sschwarze 	    MDOC_Fo != n->tok &&
11929a98b8a1Sschwarze 	    MDOC_Fn != n->tok) {
11939a98b8a1Sschwarze 		term_newln(p);
11949a98b8a1Sschwarze 		return;
11959a98b8a1Sschwarze 	}
11969a98b8a1Sschwarze 
11979a98b8a1Sschwarze 	/*
11989a98b8a1Sschwarze 	 * If we're one of the SYNOPSIS set and non-like pair-wise after
11999a98b8a1Sschwarze 	 * another (or Fn/Fo, which we've let slip through) then assert
12009a98b8a1Sschwarze 	 * vertical space, else only newline and move on.
12019a98b8a1Sschwarze 	 */
12027ebbefbeSschwarze 	switch (np->tok) {
120349aff9f8Sschwarze 	case MDOC_Fd:
120449aff9f8Sschwarze 	case MDOC_Fn:
120549aff9f8Sschwarze 	case MDOC_Fo:
120649aff9f8Sschwarze 	case MDOC_In:
120749aff9f8Sschwarze 	case MDOC_Vt:
12089a98b8a1Sschwarze 		term_vspace(p);
12099a98b8a1Sschwarze 		break;
121049aff9f8Sschwarze 	case MDOC_Ft:
12117ebbefbeSschwarze 		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
12129a98b8a1Sschwarze 			term_vspace(p);
12139a98b8a1Sschwarze 			break;
12149a98b8a1Sschwarze 		}
12159a98b8a1Sschwarze 		/* FALLTHROUGH */
12169a98b8a1Sschwarze 	default:
12179a98b8a1Sschwarze 		term_newln(p);
12189a98b8a1Sschwarze 		break;
12199a98b8a1Sschwarze 	}
12209a98b8a1Sschwarze }
12219a98b8a1Sschwarze 
12228521b0bcSschwarze static int
termp_vt_pre(DECL_ARGS)12238521b0bcSschwarze termp_vt_pre(DECL_ARGS)
12248521b0bcSschwarze {
12250ac7e6ecSschwarze 	switch (n->type) {
12260ac7e6ecSschwarze 	case ROFFT_ELEM:
12270ac7e6ecSschwarze 		return termp_ft_pre(p, pair, meta, n);
12280ac7e6ecSschwarze 	case ROFFT_BLOCK:
12299a98b8a1Sschwarze 		synopsis_pre(p, n);
1230526e306bSschwarze 		return 1;
12310ac7e6ecSschwarze 	case ROFFT_HEAD:
1232526e306bSschwarze 		return 0;
12330ac7e6ecSschwarze 	default:
1234526e306bSschwarze 		return termp_under_pre(p, pair, meta, n);
12358521b0bcSschwarze 	}
12360ac7e6ecSschwarze }
12378521b0bcSschwarze 
1238f73abda9Skristaps static int
termp_bold_pre(DECL_ARGS)1239a231c1b4Sschwarze termp_bold_pre(DECL_ARGS)
1240f73abda9Skristaps {
1241fa70b73eSschwarze 	term_fontpush(p, TERMFONT_BOLD);
1242526e306bSschwarze 	return 1;
1243f73abda9Skristaps }
1244f73abda9Skristaps 
12459a98b8a1Sschwarze static int
termp_fd_pre(DECL_ARGS)12469a98b8a1Sschwarze termp_fd_pre(DECL_ARGS)
1247f73abda9Skristaps {
12489a98b8a1Sschwarze 	synopsis_pre(p, n);
1249526e306bSschwarze 	return termp_bold_pre(p, pair, meta, n);
1250f73abda9Skristaps }
1251f73abda9Skristaps 
1252d97bd30dSschwarze static void
termp_fd_post(DECL_ARGS)1253d97bd30dSschwarze termp_fd_post(DECL_ARGS)
1254d97bd30dSschwarze {
1255d97bd30dSschwarze 	term_newln(p);
1256d97bd30dSschwarze }
1257d97bd30dSschwarze 
1258f73abda9Skristaps static int
termp_sh_pre(DECL_ARGS)1259f73abda9Skristaps termp_sh_pre(DECL_ARGS)
1260f73abda9Skristaps {
12617ebbefbeSschwarze 	struct roff_node	*np;
12628c089adfSschwarze 
12638c089adfSschwarze 	switch (n->type) {
1264d1982c71Sschwarze 	case ROFFT_BLOCK:
12657fa9bcfaSschwarze 		/*
12667fa9bcfaSschwarze 		 * Vertical space before sections, except
12677fa9bcfaSschwarze 		 * when the previous section was empty.
12687fa9bcfaSschwarze 		 */
12697ebbefbeSschwarze 		if ((np = roff_node_prev(n)) == NULL ||
12707ebbefbeSschwarze 		    np->tok != MDOC_Sh ||
12717ebbefbeSschwarze 		    (np->body != NULL && np->body->child != NULL))
1272f73abda9Skristaps 			term_vspace(p);
1273e530870eSschwarze 		break;
1274d1982c71Sschwarze 	case ROFFT_HEAD:
12750ac7e6ecSschwarze 		return termp_bold_pre(p, pair, meta, n);
1276d1982c71Sschwarze 	case ROFFT_BODY:
1277e93ea447Sschwarze 		p->tcol->offset = term_len(p, p->defindent);
1278f7242c43Sschwarze 		term_tab_set(p, NULL);
1279f7242c43Sschwarze 		term_tab_set(p, "T");
1280f7242c43Sschwarze 		term_tab_set(p, ".5i");
12810ac7e6ecSschwarze 		if (n->sec == SEC_AUTHORS)
12828ba5d246Sschwarze 			p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1283f73abda9Skristaps 		break;
1284f73abda9Skristaps 	default:
1285f73abda9Skristaps 		break;
1286f73abda9Skristaps 	}
1287526e306bSschwarze 	return 1;
1288f73abda9Skristaps }
1289f73abda9Skristaps 
1290f73abda9Skristaps static void
termp_sh_post(DECL_ARGS)1291f73abda9Skristaps termp_sh_post(DECL_ARGS)
1292f73abda9Skristaps {
12938c089adfSschwarze 	switch (n->type) {
1294d1982c71Sschwarze 	case ROFFT_HEAD:
1295f73abda9Skristaps 		term_newln(p);
1296f73abda9Skristaps 		break;
1297d1982c71Sschwarze 	case ROFFT_BODY:
1298f73abda9Skristaps 		term_newln(p);
1299e93ea447Sschwarze 		p->tcol->offset = 0;
1300f73abda9Skristaps 		break;
1301f73abda9Skristaps 	default:
1302f73abda9Skristaps 		break;
1303f73abda9Skristaps 	}
1304f73abda9Skristaps }
1305f73abda9Skristaps 
1306f73abda9Skristaps static void
termp_lb_post(DECL_ARGS)1307f73abda9Skristaps termp_lb_post(DECL_ARGS)
1308f73abda9Skristaps {
13090ac7e6ecSschwarze 	if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE)
1310f73abda9Skristaps 		term_newln(p);
1311f73abda9Skristaps }
1312f73abda9Skristaps 
1313f73abda9Skristaps static int
termp_d1_pre(DECL_ARGS)1314f73abda9Skristaps termp_d1_pre(DECL_ARGS)
1315f73abda9Skristaps {
1316d1982c71Sschwarze 	if (n->type != ROFFT_BLOCK)
1317526e306bSschwarze 		return 1;
1318f73abda9Skristaps 	term_newln(p);
1319e93ea447Sschwarze 	p->tcol->offset += term_len(p, p->defindent + 1);
1320f7242c43Sschwarze 	term_tab_set(p, NULL);
1321f7242c43Sschwarze 	term_tab_set(p, "T");
1322f7242c43Sschwarze 	term_tab_set(p, ".5i");
1323526e306bSschwarze 	return 1;
1324f73abda9Skristaps }
1325f73abda9Skristaps 
1326f73abda9Skristaps static int
termp_ft_pre(DECL_ARGS)1327f73abda9Skristaps termp_ft_pre(DECL_ARGS)
1328f73abda9Skristaps {
13299a98b8a1Sschwarze 	synopsis_pre(p, n);
13300ac7e6ecSschwarze 	return termp_under_pre(p, pair, meta, n);
1331f73abda9Skristaps }
1332f73abda9Skristaps 
1333f73abda9Skristaps static int
termp_fn_pre(DECL_ARGS)1334f73abda9Skristaps termp_fn_pre(DECL_ARGS)
1335f73abda9Skristaps {
13365d8feabbSschwarze 	size_t		 rmargin = 0;
1337a35fc07aSschwarze 	int		 pretty;
1338a35fc07aSschwarze 
13399a98b8a1Sschwarze 	synopsis_pre(p, n);
13400ac7e6ecSschwarze 	pretty = n->flags & NODE_SYNPRETTY;
13410ac7e6ecSschwarze 	if ((n = n->child) == NULL)
1342526e306bSschwarze 		return 0;
1343a35fc07aSschwarze 
1344bcb72841Sschwarze 	if (pretty) {
1345e93ea447Sschwarze 		rmargin = p->tcol->rmargin;
1346e93ea447Sschwarze 		p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1347eeb5fd14Sschwarze 		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1348bcb72841Sschwarze 	}
1349bcb72841Sschwarze 
1350d1982c71Sschwarze 	assert(n->type == ROFFT_TEXT);
1351fa70b73eSschwarze 	term_fontpush(p, TERMFONT_BOLD);
1352a35fc07aSschwarze 	term_word(p, n->string);
1353fa70b73eSschwarze 	term_fontpop(p);
1354f73abda9Skristaps 
1355bcb72841Sschwarze 	if (pretty) {
1356bcb72841Sschwarze 		term_flushln(p);
1357eeb5fd14Sschwarze 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1358771c54bcSschwarze 		p->flags |= TERMP_NOPAD;
1359e93ea447Sschwarze 		p->tcol->offset = p->tcol->rmargin;
1360e93ea447Sschwarze 		p->tcol->rmargin = rmargin;
1361bcb72841Sschwarze 	}
1362bcb72841Sschwarze 
1363f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1364f73abda9Skristaps 	term_word(p, "(");
1365a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
1366f73abda9Skristaps 
1367a35fc07aSschwarze 	for (n = n->next; n; n = n->next) {
1368d1982c71Sschwarze 		assert(n->type == ROFFT_TEXT);
1369fa70b73eSschwarze 		term_fontpush(p, TERMFONT_UNDER);
13701192b926Sschwarze 		if (pretty)
13711192b926Sschwarze 			p->flags |= TERMP_NBRWORD;
1372a35fc07aSschwarze 		term_word(p, n->string);
1373fa70b73eSschwarze 		term_fontpop(p);
1374fa70b73eSschwarze 
1375a35fc07aSschwarze 		if (n->next) {
1376a35fc07aSschwarze 			p->flags |= TERMP_NOSPACE;
1377f73abda9Skristaps 			term_word(p, ",");
1378f73abda9Skristaps 		}
1379a35fc07aSschwarze 	}
1380f73abda9Skristaps 
1381a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
1382f73abda9Skristaps 	term_word(p, ")");
1383f73abda9Skristaps 
1384a35fc07aSschwarze 	if (pretty) {
1385a35fc07aSschwarze 		p->flags |= TERMP_NOSPACE;
1386f73abda9Skristaps 		term_word(p, ";");
1387bcb72841Sschwarze 		term_flushln(p);
1388a35fc07aSschwarze 	}
1389526e306bSschwarze 	return 0;
1390f73abda9Skristaps }
1391f73abda9Skristaps 
1392f73abda9Skristaps static int
termp_fa_pre(DECL_ARGS)1393f73abda9Skristaps termp_fa_pre(DECL_ARGS)
1394f73abda9Skristaps {
13953a0d07afSschwarze 	const struct roff_node	*nn;
1396f73abda9Skristaps 
13970ac7e6ecSschwarze 	if (n->parent->tok != MDOC_Fo)
13980ac7e6ecSschwarze 		return termp_under_pre(p, pair, meta, n);
13990ac7e6ecSschwarze 
14007ebbefbeSschwarze 	for (nn = n->child; nn != NULL; nn = nn->next) {
1401fa70b73eSschwarze 		term_fontpush(p, TERMFONT_UNDER);
1402740f368bSschwarze 		p->flags |= TERMP_NBRWORD;
14038c089adfSschwarze 		term_word(p, nn->string);
1404fa70b73eSschwarze 		term_fontpop(p);
14057ebbefbeSschwarze 		if (nn->next != NULL) {
1406a35fc07aSschwarze 			p->flags |= TERMP_NOSPACE;
1407f73abda9Skristaps 			term_word(p, ",");
1408f73abda9Skristaps 		}
1409a35fc07aSschwarze 	}
14107ebbefbeSschwarze 	if (n->child != NULL &&
14117ebbefbeSschwarze 	    (nn = roff_node_next(n)) != NULL &&
14127ebbefbeSschwarze 	    nn->tok == MDOC_Fa) {
14137ebbefbeSschwarze 		p->flags |= TERMP_NOSPACE;
14147ebbefbeSschwarze 		term_word(p, ",");
14157ebbefbeSschwarze 	}
1416526e306bSschwarze 	return 0;
1417f73abda9Skristaps }
1418f73abda9Skristaps 
1419f73abda9Skristaps static int
termp_bd_pre(DECL_ARGS)1420f73abda9Skristaps termp_bd_pre(DECL_ARGS)
1421f73abda9Skristaps {
142225da2733Sschwarze 	int			 offset;
1423f73abda9Skristaps 
1424d1982c71Sschwarze 	if (n->type == ROFFT_BLOCK) {
14254175bdabSschwarze 		print_bvspace(p, n, n);
1426526e306bSschwarze 		return 1;
1427d1982c71Sschwarze 	} else if (n->type == ROFFT_HEAD)
1428526e306bSschwarze 		return 0;
1429f73abda9Skristaps 
143090d52a15Sschwarze 	/* Handle the -offset argument. */
143190d52a15Sschwarze 
143290d52a15Sschwarze 	if (n->norm->Bd.offs == NULL ||
143390d52a15Sschwarze 	    ! strcmp(n->norm->Bd.offs, "left"))
143490d52a15Sschwarze 		/* nothing */;
143590d52a15Sschwarze 	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1436e93ea447Sschwarze 		p->tcol->offset += term_len(p, p->defindent + 1);
143790d52a15Sschwarze 	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1438e93ea447Sschwarze 		p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
143925da2733Sschwarze 	else {
144025da2733Sschwarze 		offset = a2width(p, n->norm->Bd.offs);
1441e93ea447Sschwarze 		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
1442e93ea447Sschwarze 			p->tcol->offset = 0;
144325da2733Sschwarze 		else if (offset < SHRT_MAX)
1444e93ea447Sschwarze 			p->tcol->offset += offset;
144525da2733Sschwarze 	}
1446f73abda9Skristaps 
1447e1e1437fSschwarze 	switch (n->norm->Bd.type) {
1448e1e1437fSschwarze 	case DISP_literal:
1449f7242c43Sschwarze 		term_tab_set(p, NULL);
1450f7242c43Sschwarze 		term_tab_set(p, "T");
1451f7242c43Sschwarze 		term_tab_set(p, "8n");
1452e1e1437fSschwarze 		break;
1453e1e1437fSschwarze 	case DISP_centered:
1454e1e1437fSschwarze 		p->flags |= TERMP_CENTER;
1455e1e1437fSschwarze 		break;
1456d39b9a9cSschwarze 	default:
1457d39b9a9cSschwarze 		break;
1458d39b9a9cSschwarze 	}
1459e1e1437fSschwarze 	return 1;
1460f73abda9Skristaps }
1461f73abda9Skristaps 
1462f73abda9Skristaps static void
termp_bd_post(DECL_ARGS)1463f73abda9Skristaps termp_bd_post(DECL_ARGS)
1464f73abda9Skristaps {
1465d1982c71Sschwarze 	if (n->type != ROFFT_BODY)
1466f73abda9Skristaps 		return;
1467e1e1437fSschwarze 	if (n->norm->Bd.type == DISP_unfilled ||
1468e1e1437fSschwarze 	    n->norm->Bd.type == DISP_literal)
146924f1eaadSschwarze 		p->flags |= TERMP_BRNEVER;
14707c1ee023Sschwarze 	p->flags |= TERMP_NOSPACE;
1471aa4c622fSschwarze 	term_newln(p);
147224f1eaadSschwarze 	p->flags &= ~TERMP_BRNEVER;
1473e1e1437fSschwarze 	if (n->norm->Bd.type == DISP_centered)
1474e1e1437fSschwarze 		p->flags &= ~TERMP_CENTER;
1475f73abda9Skristaps }
1476f73abda9Skristaps 
1477992063deSschwarze static int
termp_xx_pre(DECL_ARGS)14787bfa2b41Sschwarze termp_xx_pre(DECL_ARGS)
1479f73abda9Skristaps {
1480816c3c54Sschwarze 	if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1481816c3c54Sschwarze 		p->flags |= TERMP_PREKEEP;
1482816c3c54Sschwarze 	return 1;
1483f73abda9Skristaps }
1484f73abda9Skristaps 
1485816c3c54Sschwarze static void
termp_xx_post(DECL_ARGS)1486816c3c54Sschwarze termp_xx_post(DECL_ARGS)
1487816c3c54Sschwarze {
1488816c3c54Sschwarze 	if (n->aux == 0)
1489816c3c54Sschwarze 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1490f73abda9Skristaps }
1491f73abda9Skristaps 
1492f73abda9Skristaps static void
termp_pf_post(DECL_ARGS)1493f73abda9Skristaps termp_pf_post(DECL_ARGS)
1494f73abda9Skristaps {
14957ebbefbeSschwarze 	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1496f73abda9Skristaps 		p->flags |= TERMP_NOSPACE;
1497f73abda9Skristaps }
1498f73abda9Skristaps 
1499f73abda9Skristaps static int
termp_ss_pre(DECL_ARGS)1500f73abda9Skristaps termp_ss_pre(DECL_ARGS)
1501f73abda9Skristaps {
15028c089adfSschwarze 	switch (n->type) {
1503d1982c71Sschwarze 	case ROFFT_BLOCK:
15047ebbefbeSschwarze 		if (roff_node_prev(n) == NULL)
1505f73abda9Skristaps 			term_newln(p);
15067ebbefbeSschwarze 		else
1507f73abda9Skristaps 			term_vspace(p);
1508f73abda9Skristaps 		break;
1509d1982c71Sschwarze 	case ROFFT_HEAD:
1510e93ea447Sschwarze 		p->tcol->offset = term_len(p, (p->defindent+1)/2);
15110ac7e6ecSschwarze 		return termp_bold_pre(p, pair, meta, n);
1512d1982c71Sschwarze 	case ROFFT_BODY:
1513e93ea447Sschwarze 		p->tcol->offset = term_len(p, p->defindent);
1514f7242c43Sschwarze 		term_tab_set(p, NULL);
1515f7242c43Sschwarze 		term_tab_set(p, "T");
1516f7242c43Sschwarze 		term_tab_set(p, ".5i");
15178d5916b5Sschwarze 		break;
1518f73abda9Skristaps 	default:
1519f73abda9Skristaps 		break;
1520f73abda9Skristaps 	}
1521526e306bSschwarze 	return 1;
1522f73abda9Skristaps }
1523f73abda9Skristaps 
1524f73abda9Skristaps static void
termp_ss_post(DECL_ARGS)1525f73abda9Skristaps termp_ss_post(DECL_ARGS)
1526f73abda9Skristaps {
1527d1982c71Sschwarze 	if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1528f73abda9Skristaps 		term_newln(p);
1529f73abda9Skristaps }
1530f73abda9Skristaps 
1531f73abda9Skristaps static int
termp_in_pre(DECL_ARGS)1532f73abda9Skristaps termp_in_pre(DECL_ARGS)
1533f73abda9Skristaps {
15349a98b8a1Sschwarze 	synopsis_pre(p, n);
15350ac7e6ecSschwarze 	if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) {
15366093755cSschwarze 		term_fontpush(p, TERMFONT_BOLD);
15376093755cSschwarze 		term_word(p, "#include");
1538f73abda9Skristaps 		term_word(p, "<");
15396093755cSschwarze 	} else {
15406093755cSschwarze 		term_word(p, "<");
15416093755cSschwarze 		term_fontpush(p, TERMFONT_UNDER);
15426093755cSschwarze 	}
1543f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1544526e306bSschwarze 	return 1;
1545f73abda9Skristaps }
1546f73abda9Skristaps 
1547f73abda9Skristaps static void
termp_in_post(DECL_ARGS)1548f73abda9Skristaps termp_in_post(DECL_ARGS)
1549f73abda9Skristaps {
15500ac7e6ecSschwarze 	if (n->flags & NODE_SYNPRETTY)
1551fa70b73eSschwarze 		term_fontpush(p, TERMFONT_BOLD);
1552f8628b0fSschwarze 	p->flags |= TERMP_NOSPACE;
1553f73abda9Skristaps 	term_word(p, ">");
15540ac7e6ecSschwarze 	if (n->flags & NODE_SYNPRETTY)
1555fa70b73eSschwarze 		term_fontpop(p);
1556f73abda9Skristaps }
1557f73abda9Skristaps 
1558f73abda9Skristaps static int
termp_pp_pre(DECL_ARGS)15596561cb23Sschwarze termp_pp_pre(DECL_ARGS)
1560d92dc4efSschwarze {
1561d92dc4efSschwarze 	term_vspace(p);
1562e053e0fdSschwarze 	if (n->flags & NODE_ID)
1563e053e0fdSschwarze 		term_tag_write(n, p->line);
1564526e306bSschwarze 	return 0;
1565d92dc4efSschwarze }
1566d92dc4efSschwarze 
1567d92dc4efSschwarze static int
termp_skip_pre(DECL_ARGS)156878bbbab4Sschwarze termp_skip_pre(DECL_ARGS)
1569551cd4a8Sschwarze {
1570526e306bSschwarze 	return 0;
1571551cd4a8Sschwarze }
1572551cd4a8Sschwarze 
1573551cd4a8Sschwarze static int
termp_quote_pre(DECL_ARGS)15742c3450fcSschwarze termp_quote_pre(DECL_ARGS)
1575f73abda9Skristaps {
1576d1982c71Sschwarze 	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1577526e306bSschwarze 		return 1;
15782c3450fcSschwarze 
15792c3450fcSschwarze 	switch (n->tok) {
158049aff9f8Sschwarze 	case MDOC_Ao:
158149aff9f8Sschwarze 	case MDOC_Aq:
158230e5ee06Sschwarze 		term_word(p, n->child != NULL && n->child->next == NULL &&
1583ba61527cSschwarze 		    n->child->tok == MDOC_Mt ? "<" : "\\(la");
15842c3450fcSschwarze 		break;
158549aff9f8Sschwarze 	case MDOC_Bro:
158649aff9f8Sschwarze 	case MDOC_Brq:
15872c3450fcSschwarze 		term_word(p, "{");
15882c3450fcSschwarze 		break;
158949aff9f8Sschwarze 	case MDOC_Oo:
159049aff9f8Sschwarze 	case MDOC_Op:
159149aff9f8Sschwarze 	case MDOC_Bo:
159249aff9f8Sschwarze 	case MDOC_Bq:
15932c3450fcSschwarze 		term_word(p, "[");
15942c3450fcSschwarze 		break;
15951aeba4ffSschwarze 	case MDOC__T:
15961aeba4ffSschwarze 		/* FALLTHROUGH */
159749aff9f8Sschwarze 	case MDOC_Do:
159849aff9f8Sschwarze 	case MDOC_Dq:
1599965f5c87Sschwarze 		term_word(p, "\\(lq");
16002c3450fcSschwarze 		break;
1601551cd4a8Sschwarze 	case MDOC_En:
1602551cd4a8Sschwarze 		if (NULL == n->norm->Es ||
1603551cd4a8Sschwarze 		    NULL == n->norm->Es->child)
1604526e306bSschwarze 			return 1;
1605551cd4a8Sschwarze 		term_word(p, n->norm->Es->child->string);
1606551cd4a8Sschwarze 		break;
160749aff9f8Sschwarze 	case MDOC_Po:
160849aff9f8Sschwarze 	case MDOC_Pq:
16092c3450fcSschwarze 		term_word(p, "(");
16102c3450fcSschwarze 		break;
161149aff9f8Sschwarze 	case MDOC_Qo:
161249aff9f8Sschwarze 	case MDOC_Qq:
16132c3450fcSschwarze 		term_word(p, "\"");
16142c3450fcSschwarze 		break;
161549aff9f8Sschwarze 	case MDOC_Ql:
161649aff9f8Sschwarze 	case MDOC_So:
161749aff9f8Sschwarze 	case MDOC_Sq:
16183987ac13Sschwarze 		term_word(p, "\\(oq");
16192c3450fcSschwarze 		break;
16202c3450fcSschwarze 	default:
16212c3450fcSschwarze 		abort();
16222c3450fcSschwarze 	}
16232c3450fcSschwarze 
1624f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1625526e306bSschwarze 	return 1;
1626f73abda9Skristaps }
1627f73abda9Skristaps 
1628f73abda9Skristaps static void
termp_quote_post(DECL_ARGS)16292c3450fcSschwarze termp_quote_post(DECL_ARGS)
1630f73abda9Skristaps {
1631f73abda9Skristaps 
1632d1982c71Sschwarze 	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1633f73abda9Skristaps 		return;
16342c3450fcSschwarze 
1635f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1636f73abda9Skristaps 
16372c3450fcSschwarze 	switch (n->tok) {
163849aff9f8Sschwarze 	case MDOC_Ao:
163949aff9f8Sschwarze 	case MDOC_Aq:
164030e5ee06Sschwarze 		term_word(p, n->child != NULL && n->child->next == NULL &&
1641ba61527cSschwarze 		    n->child->tok == MDOC_Mt ? ">" : "\\(ra");
16422c3450fcSschwarze 		break;
164349aff9f8Sschwarze 	case MDOC_Bro:
164449aff9f8Sschwarze 	case MDOC_Brq:
16452c3450fcSschwarze 		term_word(p, "}");
16462c3450fcSschwarze 		break;
164749aff9f8Sschwarze 	case MDOC_Oo:
164849aff9f8Sschwarze 	case MDOC_Op:
164949aff9f8Sschwarze 	case MDOC_Bo:
165049aff9f8Sschwarze 	case MDOC_Bq:
16512c3450fcSschwarze 		term_word(p, "]");
16522c3450fcSschwarze 		break;
16531aeba4ffSschwarze 	case MDOC__T:
16541aeba4ffSschwarze 		/* FALLTHROUGH */
165549aff9f8Sschwarze 	case MDOC_Do:
165649aff9f8Sschwarze 	case MDOC_Dq:
1657965f5c87Sschwarze 		term_word(p, "\\(rq");
16582c3450fcSschwarze 		break;
1659551cd4a8Sschwarze 	case MDOC_En:
166003ed6ec9Sschwarze 		if (n->norm->Es == NULL ||
166103ed6ec9Sschwarze 		    n->norm->Es->child == NULL ||
166203ed6ec9Sschwarze 		    n->norm->Es->child->next == NULL)
166303ed6ec9Sschwarze 			p->flags &= ~TERMP_NOSPACE;
166403ed6ec9Sschwarze 		else
1665551cd4a8Sschwarze 			term_word(p, n->norm->Es->child->next->string);
1666a1d10d08Sschwarze 		break;
166749aff9f8Sschwarze 	case MDOC_Po:
166849aff9f8Sschwarze 	case MDOC_Pq:
1669f73abda9Skristaps 		term_word(p, ")");
16702c3450fcSschwarze 		break;
167149aff9f8Sschwarze 	case MDOC_Qo:
167249aff9f8Sschwarze 	case MDOC_Qq:
16732c3450fcSschwarze 		term_word(p, "\"");
16742c3450fcSschwarze 		break;
167549aff9f8Sschwarze 	case MDOC_Ql:
167649aff9f8Sschwarze 	case MDOC_So:
167749aff9f8Sschwarze 	case MDOC_Sq:
16783987ac13Sschwarze 		term_word(p, "\\(cq");
16792c3450fcSschwarze 		break;
16802c3450fcSschwarze 	default:
16812c3450fcSschwarze 		abort();
16822c3450fcSschwarze 	}
1683f73abda9Skristaps }
1684f73abda9Skristaps 
1685f73abda9Skristaps static int
termp_eo_pre(DECL_ARGS)168603ed6ec9Sschwarze termp_eo_pre(DECL_ARGS)
168703ed6ec9Sschwarze {
168803ed6ec9Sschwarze 
1689d1982c71Sschwarze 	if (n->type != ROFFT_BODY)
1690526e306bSschwarze 		return 1;
169103ed6ec9Sschwarze 
169203ed6ec9Sschwarze 	if (n->end == ENDBODY_NOT &&
169303ed6ec9Sschwarze 	    n->parent->head->child == NULL &&
169403ed6ec9Sschwarze 	    n->child != NULL &&
169503ed6ec9Sschwarze 	    n->child->end != ENDBODY_NOT)
169603ed6ec9Sschwarze 		term_word(p, "\\&");
169703ed6ec9Sschwarze 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1698c69b5c37Sschwarze 	     n->parent->head->child != NULL && (n->child != NULL ||
1699c69b5c37Sschwarze 	     (n->parent->tail != NULL && n->parent->tail->child != NULL)))
170003ed6ec9Sschwarze 		p->flags |= TERMP_NOSPACE;
170103ed6ec9Sschwarze 
1702526e306bSschwarze 	return 1;
170303ed6ec9Sschwarze }
170403ed6ec9Sschwarze 
170503ed6ec9Sschwarze static void
termp_eo_post(DECL_ARGS)170603ed6ec9Sschwarze termp_eo_post(DECL_ARGS)
170703ed6ec9Sschwarze {
170803ed6ec9Sschwarze 	int	 body, tail;
170903ed6ec9Sschwarze 
1710d1982c71Sschwarze 	if (n->type != ROFFT_BODY)
171103ed6ec9Sschwarze 		return;
171203ed6ec9Sschwarze 
171303ed6ec9Sschwarze 	if (n->end != ENDBODY_NOT) {
171403ed6ec9Sschwarze 		p->flags &= ~TERMP_NOSPACE;
171503ed6ec9Sschwarze 		return;
171603ed6ec9Sschwarze 	}
171703ed6ec9Sschwarze 
171803ed6ec9Sschwarze 	body = n->child != NULL || n->parent->head->child != NULL;
171903ed6ec9Sschwarze 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
172003ed6ec9Sschwarze 
172103ed6ec9Sschwarze 	if (body && tail)
172203ed6ec9Sschwarze 		p->flags |= TERMP_NOSPACE;
172303ed6ec9Sschwarze 	else if ( ! (body || tail))
172403ed6ec9Sschwarze 		term_word(p, "\\&");
172503ed6ec9Sschwarze 	else if ( ! tail)
172603ed6ec9Sschwarze 		p->flags &= ~TERMP_NOSPACE;
172703ed6ec9Sschwarze }
172803ed6ec9Sschwarze 
172903ed6ec9Sschwarze static int
termp_fo_pre(DECL_ARGS)1730f73abda9Skristaps termp_fo_pre(DECL_ARGS)
1731f73abda9Skristaps {
17320ac7e6ecSschwarze 	size_t rmargin;
1733740f368bSschwarze 
17340ac7e6ecSschwarze 	switch (n->type) {
17350ac7e6ecSschwarze 	case ROFFT_BLOCK:
17369a98b8a1Sschwarze 		synopsis_pre(p, n);
1737526e306bSschwarze 		return 1;
17380ac7e6ecSschwarze 	case ROFFT_BODY:
1739e93ea447Sschwarze 		rmargin = p->tcol->rmargin;
17400ac7e6ecSschwarze 		if (n->flags & NODE_SYNPRETTY) {
1741e93ea447Sschwarze 			p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1742eeb5fd14Sschwarze 			p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1743eeb5fd14Sschwarze 					TERMP_HANG;
1744740f368bSschwarze 		}
1745997e4942Sschwarze 		p->flags |= TERMP_NOSPACE;
1746f73abda9Skristaps 		term_word(p, "(");
1747a35fc07aSschwarze 		p->flags |= TERMP_NOSPACE;
17480ac7e6ecSschwarze 		if (n->flags & NODE_SYNPRETTY) {
1749740f368bSschwarze 			term_flushln(p);
1750eeb5fd14Sschwarze 			p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1751eeb5fd14Sschwarze 					TERMP_HANG);
1752771c54bcSschwarze 			p->flags |= TERMP_NOPAD;
1753e93ea447Sschwarze 			p->tcol->offset = p->tcol->rmargin;
1754e93ea447Sschwarze 			p->tcol->rmargin = rmargin;
1755740f368bSschwarze 		}
1756526e306bSschwarze 		return 1;
17570ac7e6ecSschwarze 	default:
17580ac7e6ecSschwarze 		return termp_bold_pre(p, pair, meta, n);
1759f73abda9Skristaps 	}
1760f73abda9Skristaps }
1761f73abda9Skristaps 
1762f73abda9Skristaps static void
termp_fo_post(DECL_ARGS)1763f73abda9Skristaps termp_fo_post(DECL_ARGS)
1764f73abda9Skristaps {
1765d1982c71Sschwarze 	if (n->type != ROFFT_BODY)
17669a98b8a1Sschwarze 		return;
17679a98b8a1Sschwarze 
1768a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
1769f73abda9Skristaps 	term_word(p, ")");
17709a98b8a1Sschwarze 
17710ac7e6ecSschwarze 	if (n->flags & NODE_SYNPRETTY) {
1772a35fc07aSschwarze 		p->flags |= TERMP_NOSPACE;
1773f73abda9Skristaps 		term_word(p, ";");
1774740f368bSschwarze 		term_flushln(p);
17756093755cSschwarze 	}
1776a35fc07aSschwarze }
1777f73abda9Skristaps 
1778f73abda9Skristaps static int
termp_bf_pre(DECL_ARGS)1779f73abda9Skristaps termp_bf_pre(DECL_ARGS)
1780f73abda9Skristaps {
17810ac7e6ecSschwarze 	switch (n->type) {
17820ac7e6ecSschwarze 	case ROFFT_HEAD:
1783526e306bSschwarze 		return 0;
17840ac7e6ecSschwarze 	case ROFFT_BODY:
17850ac7e6ecSschwarze 		break;
17860ac7e6ecSschwarze 	default:
1787526e306bSschwarze 		return 1;
17880ac7e6ecSschwarze 	}
17890ac7e6ecSschwarze 	switch (n->norm->Bf.font) {
17900ac7e6ecSschwarze 	case FONT_Em:
17910ac7e6ecSschwarze 		return termp_under_pre(p, pair, meta, n);
17920ac7e6ecSschwarze 	case FONT_Sy:
17930ac7e6ecSschwarze 		return termp_bold_pre(p, pair, meta, n);
17940ac7e6ecSschwarze 	default:
17950ac7e6ecSschwarze 		return termp_li_pre(p, pair, meta, n);
17960ac7e6ecSschwarze 	}
1797f73abda9Skristaps }
1798f73abda9Skristaps 
1799f73abda9Skristaps static int
termp_sm_pre(DECL_ARGS)1800f73abda9Skristaps termp_sm_pre(DECL_ARGS)
1801f73abda9Skristaps {
18020ac7e6ecSschwarze 	if (n->child == NULL)
1803f9e7bf99Sschwarze 		p->flags ^= TERMP_NONOSPACE;
18040ac7e6ecSschwarze 	else if (strcmp(n->child->string, "on") == 0)
1805f73abda9Skristaps 		p->flags &= ~TERMP_NONOSPACE;
1806f9e7bf99Sschwarze 	else
1807f73abda9Skristaps 		p->flags |= TERMP_NONOSPACE;
1808f73abda9Skristaps 
1809f9e7bf99Sschwarze 	if (p->col && ! (TERMP_NONOSPACE & p->flags))
1810f9e7bf99Sschwarze 		p->flags &= ~TERMP_NOSPACE;
1811f9e7bf99Sschwarze 
1812526e306bSschwarze 	return 0;
1813f73abda9Skristaps }
1814f73abda9Skristaps 
1815f73abda9Skristaps static int
termp_ap_pre(DECL_ARGS)1816f73abda9Skristaps termp_ap_pre(DECL_ARGS)
1817f73abda9Skristaps {
1818f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
18192c3450fcSschwarze 	term_word(p, "'");
1820f73abda9Skristaps 	p->flags |= TERMP_NOSPACE;
1821526e306bSschwarze 	return 1;
1822f73abda9Skristaps }
1823f73abda9Skristaps 
1824f73abda9Skristaps static void
termp____post(DECL_ARGS)1825f73abda9Skristaps termp____post(DECL_ARGS)
1826f73abda9Skristaps {
18277ebbefbeSschwarze 	struct roff_node *nn;
1828f73abda9Skristaps 
1829bf782e42Sschwarze 	/*
1830bf782e42Sschwarze 	 * Handle lists of authors.  In general, print each followed by
1831bf782e42Sschwarze 	 * a comma.  Don't print the comma if there are only two
1832bf782e42Sschwarze 	 * authors.
1833bf782e42Sschwarze 	 */
18347ebbefbeSschwarze 	if (n->tok == MDOC__A &&
18357ebbefbeSschwarze 	    (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A &&
18367ebbefbeSschwarze 	    ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) &&
18377ebbefbeSschwarze 	    ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A))
1838bf782e42Sschwarze 		return;
1839bf782e42Sschwarze 
1840b822ca0dSschwarze 	/* TODO: %U. */
1841b822ca0dSschwarze 
18427ebbefbeSschwarze 	if (n->parent == NULL || n->parent->tok != MDOC_Rs)
1843d967b250Sschwarze 		return;
1844d967b250Sschwarze 
1845a35fc07aSschwarze 	p->flags |= TERMP_NOSPACE;
18467ebbefbeSschwarze 	if (roff_node_next(n) == NULL) {
1847d967b250Sschwarze 		term_word(p, ".");
1848d967b250Sschwarze 		p->flags |= TERMP_SENTENCE;
1849d967b250Sschwarze 	} else
1850d967b250Sschwarze 		term_word(p, ",");
1851f73abda9Skristaps }
1852f73abda9Skristaps 
1853f73abda9Skristaps static int
termp_li_pre(DECL_ARGS)1854fa70b73eSschwarze termp_li_pre(DECL_ARGS)
1855fa70b73eSschwarze {
1856fa70b73eSschwarze 	term_fontpush(p, TERMFONT_NONE);
1857526e306bSschwarze 	return 1;
1858fa70b73eSschwarze }
1859fa70b73eSschwarze 
1860fa70b73eSschwarze static int
termp_lk_pre(DECL_ARGS)1861f73abda9Skristaps termp_lk_pre(DECL_ARGS)
1862f73abda9Skristaps {
1863eb4d0f30Sschwarze 	const struct roff_node *link, *descr, *punct;
1864f73abda9Skristaps 
186596aebfb3Sschwarze 	if ((link = n->child) == NULL)
1866526e306bSschwarze 		return 0;
1867cfd12f23Sschwarze 
1868eb4d0f30Sschwarze 	/* Find beginning of trailing punctuation. */
1869eb4d0f30Sschwarze 	punct = n->last;
1870eb4d0f30Sschwarze 	while (punct != link && punct->flags & NODE_DELIMC)
1871eb4d0f30Sschwarze 		punct = punct->prev;
1872eb4d0f30Sschwarze 	punct = punct->next;
1873eb4d0f30Sschwarze 
187496aebfb3Sschwarze 	/* Link text. */
1875eb4d0f30Sschwarze 	if ((descr = link->next) != NULL && descr != punct) {
1876fa70b73eSschwarze 		term_fontpush(p, TERMFONT_UNDER);
1877eb4d0f30Sschwarze 		while (descr != punct) {
1878eb4d0f30Sschwarze 			if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1879eb4d0f30Sschwarze 				p->flags |= TERMP_NOSPACE;
1880cfd12f23Sschwarze 			term_word(p, descr->string);
1881cfd12f23Sschwarze 			descr = descr->next;
1882cfd12f23Sschwarze 		}
1883646f82abSschwarze 		term_fontpop(p);
1884a35fc07aSschwarze 		p->flags |= TERMP_NOSPACE;
1885f73abda9Skristaps 		term_word(p, ":");
1886cfd12f23Sschwarze 	}
1887f73abda9Skristaps 
188896aebfb3Sschwarze 	/* Link target. */
1889fa70b73eSschwarze 	term_fontpush(p, TERMFONT_BOLD);
1890cfd12f23Sschwarze 	term_word(p, link->string);
1891fa70b73eSschwarze 	term_fontpop(p);
1892f73abda9Skristaps 
189396aebfb3Sschwarze 	/* Trailing punctuation. */
1894eb4d0f30Sschwarze 	while (punct != NULL) {
189596aebfb3Sschwarze 		p->flags |= TERMP_NOSPACE;
1896eb4d0f30Sschwarze 		term_word(p, punct->string);
1897eb4d0f30Sschwarze 		punct = punct->next;
189896aebfb3Sschwarze 	}
1899526e306bSschwarze 	return 0;
1900f73abda9Skristaps }
1901f73abda9Skristaps 
1902f73abda9Skristaps static int
termp_bk_pre(DECL_ARGS)19031eead9a6Sschwarze termp_bk_pre(DECL_ARGS)
19041eead9a6Sschwarze {
1905c861dd6fSschwarze 	switch (n->type) {
1906d1982c71Sschwarze 	case ROFFT_BLOCK:
1907769ee804Sschwarze 		break;
1908d1982c71Sschwarze 	case ROFFT_HEAD:
1909526e306bSschwarze 		return 0;
1910d1982c71Sschwarze 	case ROFFT_BODY:
191130e5ee06Sschwarze 		if (n->parent->args != NULL || n->prev->child == NULL)
19121eead9a6Sschwarze 			p->flags |= TERMP_PREKEEP;
1913769ee804Sschwarze 		break;
1914c861dd6fSschwarze 	default:
1915c861dd6fSschwarze 		abort();
1916c861dd6fSschwarze 	}
1917526e306bSschwarze 	return 1;
19181eead9a6Sschwarze }
19191eead9a6Sschwarze 
19201eead9a6Sschwarze static void
termp_bk_post(DECL_ARGS)19211eead9a6Sschwarze termp_bk_post(DECL_ARGS)
19221eead9a6Sschwarze {
1923d1982c71Sschwarze 	if (n->type == ROFFT_BODY)
19241eead9a6Sschwarze 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
19251eead9a6Sschwarze }
19261eead9a6Sschwarze 
19270ac7e6ecSschwarze /*
19280ac7e6ecSschwarze  * If we are in an `Rs' and there is a journal present,
19290ac7e6ecSschwarze  * then quote us instead of underlining us (for disambiguation).
19300ac7e6ecSschwarze  */
19316f207799Sschwarze static void
termp__t_post(DECL_ARGS)19326f207799Sschwarze termp__t_post(DECL_ARGS)
19336f207799Sschwarze {
19340ac7e6ecSschwarze 	if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
19355d273f35Sschwarze 	    n->parent->norm->Rs.quote_T)
19367ead8a4eSschwarze 		termp_quote_post(p, pair, meta, n);
19377ead8a4eSschwarze 	termp____post(p, pair, meta, n);
19386f207799Sschwarze }
19396f207799Sschwarze 
19406f207799Sschwarze static int
termp__t_pre(DECL_ARGS)19416f207799Sschwarze termp__t_pre(DECL_ARGS)
19426f207799Sschwarze {
19430ac7e6ecSschwarze 	if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
19445d273f35Sschwarze 	    n->parent->norm->Rs.quote_T)
1945526e306bSschwarze 		return termp_quote_pre(p, pair, meta, n);
19460ac7e6ecSschwarze 	else
19470ac7e6ecSschwarze 		return termp_under_pre(p, pair, meta, n);
19486f207799Sschwarze }
19496f207799Sschwarze 
19501eead9a6Sschwarze static int
termp_under_pre(DECL_ARGS)1951a231c1b4Sschwarze termp_under_pre(DECL_ARGS)
1952f73abda9Skristaps {
1953fa70b73eSschwarze 	term_fontpush(p, TERMFONT_UNDER);
1954526e306bSschwarze 	return 1;
1955f73abda9Skristaps }
1956c0a657b3Sschwarze 
1957c0a657b3Sschwarze static int
termp_abort_pre(DECL_ARGS)19587c539ecbSschwarze termp_abort_pre(DECL_ARGS)
19597c539ecbSschwarze {
19607c539ecbSschwarze 	abort();
19617c539ecbSschwarze }
1962