xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision 723822f6bef0f4e1fd3e1afda52bd811abb48d9d)
1*723822f6Sschwarze /* $OpenBSD: mdoc_man.c,v 1.137 2025/01/24 22:36:51 schwarze Exp $ */
275d4d0e5Sschwarze /*
3*723822f6Sschwarze  * Copyright (c) 2011-2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
475d4d0e5Sschwarze  *
575d4d0e5Sschwarze  * Permission to use, copy, modify, and distribute this software for any
675d4d0e5Sschwarze  * purpose with or without fee is hereby granted, provided that the above
775d4d0e5Sschwarze  * copyright notice and this permission notice appear in all copies.
875d4d0e5Sschwarze  *
975d4d0e5Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1075d4d0e5Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1175d4d0e5Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1275d4d0e5Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1375d4d0e5Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1475d4d0e5Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1575d4d0e5Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1675d4d0e5Sschwarze  */
1790d52a15Sschwarze #include <sys/types.h>
1890d52a15Sschwarze 
19c8addc6fSschwarze #include <assert.h>
2075d4d0e5Sschwarze #include <stdio.h>
2129478532Sschwarze #include <stdlib.h>
2275d4d0e5Sschwarze #include <string.h>
2375d4d0e5Sschwarze 
244f4f7972Sschwarze #include "mandoc_aux.h"
25d1982c71Sschwarze #include "mandoc.h"
26d1982c71Sschwarze #include "roff.h"
2775d4d0e5Sschwarze #include "mdoc.h"
28d1982c71Sschwarze #include "man.h"
29d1982c71Sschwarze #include "out.h"
3075d4d0e5Sschwarze #include "main.h"
3175d4d0e5Sschwarze 
322a238f45Sschwarze #define	DECL_ARGS const struct roff_meta *meta, struct roff_node *n
3375d4d0e5Sschwarze 
34644b390bSschwarze typedef	int	(*int_fp)(DECL_ARGS);
35644b390bSschwarze typedef	void	(*void_fp)(DECL_ARGS);
36644b390bSschwarze 
3716fe0cfcSschwarze struct	mdoc_man_act {
38644b390bSschwarze 	int_fp		  cond; /* DON'T run actions */
39644b390bSschwarze 	int_fp		  pre; /* pre-node action */
40644b390bSschwarze 	void_fp		  post; /* post-node action */
41ca0ce676Sschwarze 	const char	 *prefix; /* pre-node string constant */
42ca0ce676Sschwarze 	const char	 *suffix; /* post-node string constant */
43ca0ce676Sschwarze };
4475d4d0e5Sschwarze 
4575d4d0e5Sschwarze static	int	  cond_body(DECL_ARGS);
46ca0ce676Sschwarze static	int	  cond_head(DECL_ARGS);
47388939aeSschwarze static  void	  font_push(char);
48388939aeSschwarze static	void	  font_pop(void);
499f97fb19Sschwarze static	int	  man_strlen(const char *);
50a14e0b20Sschwarze static	void	  mid_it(void);
51e3ceda8aSschwarze static	void	  post__t(DECL_ARGS);
5227d19a00Sschwarze static	void	  post_aq(DECL_ARGS);
53ca0ce676Sschwarze static	void	  post_bd(DECL_ARGS);
540129f95fSschwarze static	void	  post_bf(DECL_ARGS);
558b4da3beSschwarze static	void	  post_bk(DECL_ARGS);
565eced068Sschwarze static	void	  post_bl(DECL_ARGS);
57ca0ce676Sschwarze static	void	  post_dl(DECL_ARGS);
58551cd4a8Sschwarze static	void	  post_en(DECL_ARGS);
5975d4d0e5Sschwarze static	void	  post_enc(DECL_ARGS);
60c19c703eSschwarze static	void	  post_eo(DECL_ARGS);
61e2ee3b43Sschwarze static	void	  post_fa(DECL_ARGS);
62d97bd30dSschwarze static	void	  post_fd(DECL_ARGS);
63388939aeSschwarze static	void	  post_fl(DECL_ARGS);
640df06370Sschwarze static	void	  post_fn(DECL_ARGS);
65e2ee3b43Sschwarze static	void	  post_fo(DECL_ARGS);
66388939aeSschwarze static	void	  post_font(DECL_ARGS);
670e0aff12Sschwarze static	void	  post_in(DECL_ARGS);
685eced068Sschwarze static	void	  post_it(DECL_ARGS);
69835b952bSschwarze static	void	  post_lb(DECL_ARGS);
70ca0ce676Sschwarze static	void	  post_nm(DECL_ARGS);
7175d4d0e5Sschwarze static	void	  post_percent(DECL_ARGS);
72ca0ce676Sschwarze static	void	  post_pf(DECL_ARGS);
73e6ab379cSschwarze static	void	  post_sect(DECL_ARGS);
74bce51a71Sschwarze static	void	  post_vt(DECL_ARGS);
75e3ceda8aSschwarze static	int	  pre__t(DECL_ARGS);
767c539ecbSschwarze static	int	  pre_abort(DECL_ARGS);
778ba5d246Sschwarze static	int	  pre_an(DECL_ARGS);
78e6ab379cSschwarze static	int	  pre_ap(DECL_ARGS);
7927d19a00Sschwarze static	int	  pre_aq(DECL_ARGS);
80e6ab379cSschwarze static	int	  pre_bd(DECL_ARGS);
810129f95fSschwarze static	int	  pre_bf(DECL_ARGS);
828b4da3beSschwarze static	int	  pre_bk(DECL_ARGS);
835eced068Sschwarze static	int	  pre_bl(DECL_ARGS);
84644b390bSschwarze static	void	  pre_br(DECL_ARGS);
8575d4d0e5Sschwarze static	int	  pre_dl(DECL_ARGS);
86551cd4a8Sschwarze static	int	  pre_en(DECL_ARGS);
87ca0ce676Sschwarze static	int	  pre_enc(DECL_ARGS);
88388939aeSschwarze static	int	  pre_em(DECL_ARGS);
8978bbbab4Sschwarze static	int	  pre_skip(DECL_ARGS);
907d18a139Sschwarze static	int	  pre_eo(DECL_ARGS);
91e214f641Sschwarze static	int	  pre_ex(DECL_ARGS);
92e2ee3b43Sschwarze static	int	  pre_fa(DECL_ARGS);
93d97bd30dSschwarze static	int	  pre_fd(DECL_ARGS);
94388939aeSschwarze static	int	  pre_fl(DECL_ARGS);
950df06370Sschwarze static	int	  pre_fn(DECL_ARGS);
96e2ee3b43Sschwarze static	int	  pre_fo(DECL_ARGS);
97644b390bSschwarze static	void	  pre_ft(DECL_ARGS);
98c4d3fa85Sschwarze static	int	  pre_Ft(DECL_ARGS);
990e0aff12Sschwarze static	int	  pre_in(DECL_ARGS);
10075d4d0e5Sschwarze static	int	  pre_it(DECL_ARGS);
101cfd12f23Sschwarze static	int	  pre_lk(DECL_ARGS);
102388939aeSschwarze static	int	  pre_li(DECL_ARGS);
10375d4d0e5Sschwarze static	int	  pre_nm(DECL_ARGS);
104d4adcee8Sschwarze static	int	  pre_no(DECL_ARGS);
105b7fc66beSschwarze static	void	  pre_noarg(DECL_ARGS);
10675d4d0e5Sschwarze static	int	  pre_ns(DECL_ARGS);
10711d70615Sschwarze static	void	  pre_onearg(DECL_ARGS);
10875d4d0e5Sschwarze static	int	  pre_pp(DECL_ARGS);
109e3ceda8aSschwarze static	int	  pre_rs(DECL_ARGS);
110c8addc6fSschwarze static	int	  pre_sm(DECL_ARGS);
1116561cb23Sschwarze static	void	  pre_sp(DECL_ARGS);
112ca0ce676Sschwarze static	int	  pre_sect(DECL_ARGS);
113388939aeSschwarze static	int	  pre_sy(DECL_ARGS);
1147ebbefbeSschwarze static	void	  pre_syn(struct roff_node *);
115f7242c43Sschwarze static	void	  pre_ta(DECL_ARGS);
116bce51a71Sschwarze static	int	  pre_vt(DECL_ARGS);
11775d4d0e5Sschwarze static	int	  pre_xr(DECL_ARGS);
11851c88ffdSschwarze static	void	  print_word(const char *);
11954cad943Sschwarze static	void	  print_line(const char *, int);
12054cad943Sschwarze static	void	  print_block(const char *, int);
12190d52a15Sschwarze static	void	  print_offs(const char *, int);
12225da2733Sschwarze static	void	  print_width(const struct mdoc_bl *,
1233a0d07afSschwarze 			const struct roff_node *);
1245eced068Sschwarze static	void	  print_count(int *);
125ca0ce676Sschwarze static	void	  print_node(DECL_ARGS);
12675d4d0e5Sschwarze 
12716fe0cfcSschwarze static const void_fp roff_man_acts[ROFF_MAX] = {
1286de096f4Sschwarze 	pre_br,		/* br */
1296de096f4Sschwarze 	pre_onearg,	/* ce */
130b7fc66beSschwarze 	pre_noarg,	/* fi */
1316de096f4Sschwarze 	pre_ft,		/* ft */
1326de096f4Sschwarze 	pre_onearg,	/* ll */
1336de096f4Sschwarze 	pre_onearg,	/* mc */
134b7fc66beSschwarze 	pre_noarg,	/* nf */
135af1e8f15Sschwarze 	pre_onearg,	/* po */
1366de096f4Sschwarze 	pre_onearg,	/* rj */
1376de096f4Sschwarze 	pre_sp,		/* sp */
1386de096f4Sschwarze 	pre_ta,		/* ta */
1396de096f4Sschwarze 	pre_onearg,	/* ti */
140644b390bSschwarze };
141644b390bSschwarze 
14216fe0cfcSschwarze static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = {
143e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
144e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
145a0c8580aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
146e6ab379cSschwarze 	{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
147e6ab379cSschwarze 	{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
14875d4d0e5Sschwarze 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
149e6ab379cSschwarze 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
15075d4d0e5Sschwarze 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
151e6ab379cSschwarze 	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
152e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
1535eced068Sschwarze 	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
154e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
1555eced068Sschwarze 	{ NULL, pre_it, post_it, NULL, NULL }, /* It */
156388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
1578ba5d246Sschwarze 	{ NULL, pre_an, NULL, NULL, NULL }, /* An */
15814a309e3Sschwarze 	{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
159388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
160388939aeSschwarze 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
161388939aeSschwarze 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
162388939aeSschwarze 	{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
163388939aeSschwarze 	{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
164388939aeSschwarze 	{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
165e214f641Sschwarze 	{ NULL, pre_ex, NULL, NULL, NULL }, /* Ex */
166e2ee3b43Sschwarze 	{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
167d97bd30dSschwarze 	{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
168388939aeSschwarze 	{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
1690df06370Sschwarze 	{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
170c4d3fa85Sschwarze 	{ NULL, pre_Ft, post_font, NULL, NULL }, /* Ft */
171388939aeSschwarze 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
1720e0aff12Sschwarze 	{ NULL, pre_in, post_in, NULL, NULL }, /* In */
173388939aeSschwarze 	{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
17475d4d0e5Sschwarze 	{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
17575d4d0e5Sschwarze 	{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
17675d4d0e5Sschwarze 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
1777c539ecbSschwarze 	{ NULL, pre_abort, NULL, NULL, NULL }, /* Ot */
178388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
1798ccddcd3Sschwarze 	{ NULL, pre_ex, NULL, NULL, NULL }, /* Rv */
180a0c8580aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
181388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
182bce51a71Sschwarze 	{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
183a0c8580aSschwarze 	{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
184e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
185e3ceda8aSschwarze 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
186e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
187e3ceda8aSschwarze 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
188e3ceda8aSschwarze 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
189e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
190e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
191e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
192e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
193e3ceda8aSschwarze 	{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
194e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
195f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
19627d19a00Sschwarze 	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Ao */
19727d19a00Sschwarze 	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Aq */
198a0c8580aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
199e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
2000129f95fSschwarze 	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
201e6ab379cSschwarze 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
202e6ab379cSschwarze 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
203f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bsx */
204f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bx */
20578bbbab4Sschwarze 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
206f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
20777f49befSschwarze 	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
20877f49befSschwarze 	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
209c19c703eSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
210c19c703eSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
211388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
2127d18a139Sschwarze 	{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
213f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Fx */
214388939aeSschwarze 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
215d4adcee8Sschwarze 	{ NULL, pre_no, NULL, NULL, NULL }, /* No */
21675d4d0e5Sschwarze 	{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
217f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Nx */
218f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Ox */
219e6ab379cSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
220e6ab379cSschwarze 	{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
221e6ab379cSschwarze 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
222e6ab379cSschwarze 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
223f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
2243987ac13Sschwarze 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
225f3042e3bSschwarze 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
226f3042e3bSschwarze 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
227f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
228e3ceda8aSschwarze 	{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
229f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
2303987ac13Sschwarze 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
2313987ac13Sschwarze 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
232c8addc6fSschwarze 	{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
233388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
234388939aeSschwarze 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
235388939aeSschwarze 	{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
236816c3c54Sschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ux */
237e3ceda8aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
238e3ceda8aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
239e2ee3b43Sschwarze 	{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
240e2ee3b43Sschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
241e6ab379cSschwarze 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
242f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
2438b4da3beSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
2448b4da3beSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
2458ccddcd3Sschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Bt */
246a0c8580aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
247551cd4a8Sschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
2488ccddcd3Sschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
249835b952bSschwarze 	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
2507c539ecbSschwarze 	{ NULL, pre_abort, NULL, NULL, NULL }, /* Lp */
251cfd12f23Sschwarze 	{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
252388939aeSschwarze 	{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
253f3042e3bSschwarze 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
254f3042e3bSschwarze 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
255f3042e3bSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
256e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
25778bbbab4Sschwarze 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
258551cd4a8Sschwarze 	{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
259f54c3aafSschwarze 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Dx */
260e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
261e3ceda8aSschwarze 	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
262e3ceda8aSschwarze 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
2638b1bef1aSschwarze 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Tg */
26475d4d0e5Sschwarze };
26516fe0cfcSschwarze static const struct mdoc_man_act *mdoc_man_act(enum roff_tok);
26675d4d0e5Sschwarze 
26751c88ffdSschwarze static	int		outflags;
26854cad943Sschwarze #define	MMAN_spc	(1 << 0)  /* blank character before next word */
26954cad943Sschwarze #define	MMAN_spc_force	(1 << 1)  /* even before trailing punctuation */
27054cad943Sschwarze #define	MMAN_nl		(1 << 2)  /* break man(7) code line */
27154cad943Sschwarze #define	MMAN_br		(1 << 3)  /* break output line */
27254cad943Sschwarze #define	MMAN_sp		(1 << 4)  /* insert a blank output line */
27354cad943Sschwarze #define	MMAN_PP		(1 << 5)  /* reset indentation etc. */
27454cad943Sschwarze #define	MMAN_Sm		(1 << 6)  /* horizontal spacing mode */
27554cad943Sschwarze #define	MMAN_Bk		(1 << 7)  /* word keep mode */
276f54686a1Sschwarze #define	MMAN_Bk_susp	(1 << 8)  /* suspend this (after a macro) */
277f54686a1Sschwarze #define	MMAN_An_split	(1 << 9)  /* author mode is "split" */
278f54686a1Sschwarze #define	MMAN_An_nosplit	(1 << 10) /* author mode is "nosplit" */
279f54686a1Sschwarze #define	MMAN_PD		(1 << 11) /* inter-paragraph spacing disabled */
2801192b926Sschwarze #define	MMAN_nbrword	(1 << 12) /* do not break the next word */
28151c88ffdSschwarze 
2825ee03fc6Sschwarze #define	BL_STACK_MAX	32
2835ee03fc6Sschwarze 
28425da2733Sschwarze static	int		Bl_stack[BL_STACK_MAX];  /* offsets [chars] */
2855ee03fc6Sschwarze static	int		Bl_stack_post[BL_STACK_MAX];  /* add final .RE */
2865ee03fc6Sschwarze static	int		Bl_stack_len;  /* number of nested Bl blocks */
28780a412c7Sschwarze static	int		TPremain;  /* characters before tag is full */
28880a412c7Sschwarze 
289388939aeSschwarze static	struct {
290388939aeSschwarze 	char	*head;
291388939aeSschwarze 	char	*tail;
292388939aeSschwarze 	size_t	 size;
293388939aeSschwarze }	fontqueue;
294388939aeSschwarze 
29549aff9f8Sschwarze 
29616fe0cfcSschwarze static const struct mdoc_man_act *
29716fe0cfcSschwarze mdoc_man_act(enum roff_tok tok)
29816fe0cfcSschwarze {
29916fe0cfcSschwarze 	assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
30016fe0cfcSschwarze 	return mdoc_man_acts + (tok - MDOC_Dd);
30116fe0cfcSschwarze }
30216fe0cfcSschwarze 
3039f97fb19Sschwarze static int
3049f97fb19Sschwarze man_strlen(const char *cp)
3059f97fb19Sschwarze {
3069f97fb19Sschwarze 	size_t	 rsz;
3079f97fb19Sschwarze 	int	 skip, sz;
3089f97fb19Sschwarze 
3099f97fb19Sschwarze 	sz = 0;
3109f97fb19Sschwarze 	skip = 0;
3119f97fb19Sschwarze 	for (;;) {
3129f97fb19Sschwarze 		rsz = strcspn(cp, "\\");
3139f97fb19Sschwarze 		if (rsz) {
3149f97fb19Sschwarze 			cp += rsz;
3159f97fb19Sschwarze 			if (skip) {
3169f97fb19Sschwarze 				skip = 0;
3179f97fb19Sschwarze 				rsz--;
3189f97fb19Sschwarze 			}
3199f97fb19Sschwarze 			sz += rsz;
3209f97fb19Sschwarze 		}
3219f97fb19Sschwarze 		if ('\0' == *cp)
3229f97fb19Sschwarze 			break;
3239f97fb19Sschwarze 		cp++;
3249f97fb19Sschwarze 		switch (mandoc_escape(&cp, NULL, NULL)) {
3259f97fb19Sschwarze 		case ESCAPE_ERROR:
3269f97fb19Sschwarze 			return sz;
3279f97fb19Sschwarze 		case ESCAPE_UNICODE:
3289f97fb19Sschwarze 		case ESCAPE_NUMBERED:
3299f97fb19Sschwarze 		case ESCAPE_SPECIAL:
3306f6722cbSschwarze 		case ESCAPE_UNDEF:
3319f97fb19Sschwarze 		case ESCAPE_OVERSTRIKE:
3329f97fb19Sschwarze 			if (skip)
3339f97fb19Sschwarze 				skip = 0;
3349f97fb19Sschwarze 			else
3359f97fb19Sschwarze 				sz++;
3369f97fb19Sschwarze 			break;
3379f97fb19Sschwarze 		case ESCAPE_SKIPCHAR:
3389f97fb19Sschwarze 			skip = 1;
3399f97fb19Sschwarze 			break;
3409f97fb19Sschwarze 		default:
3419f97fb19Sschwarze 			break;
3429f97fb19Sschwarze 		}
3439f97fb19Sschwarze 	}
3449f97fb19Sschwarze 	return sz;
3459f97fb19Sschwarze }
3469f97fb19Sschwarze 
347388939aeSschwarze static void
348388939aeSschwarze font_push(char newfont)
349388939aeSschwarze {
350388939aeSschwarze 
351388939aeSschwarze 	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
352388939aeSschwarze 		fontqueue.size += 8;
353388939aeSschwarze 		fontqueue.head = mandoc_realloc(fontqueue.head,
354388939aeSschwarze 		    fontqueue.size);
355388939aeSschwarze 	}
356388939aeSschwarze 	*fontqueue.tail = newfont;
35780a412c7Sschwarze 	print_word("");
35880a412c7Sschwarze 	printf("\\f");
359388939aeSschwarze 	putchar(newfont);
360388939aeSschwarze 	outflags &= ~MMAN_spc;
361388939aeSschwarze }
362388939aeSschwarze 
363388939aeSschwarze static void
364388939aeSschwarze font_pop(void)
365388939aeSschwarze {
366388939aeSschwarze 
367388939aeSschwarze 	if (fontqueue.tail > fontqueue.head)
368388939aeSschwarze 		fontqueue.tail--;
369388939aeSschwarze 	outflags &= ~MMAN_spc;
37080a412c7Sschwarze 	print_word("");
37180a412c7Sschwarze 	printf("\\f");
372388939aeSschwarze 	putchar(*fontqueue.tail);
373388939aeSschwarze }
374388939aeSschwarze 
37575d4d0e5Sschwarze static void
37651c88ffdSschwarze print_word(const char *s)
37775d4d0e5Sschwarze {
378ca0ce676Sschwarze 
37954cad943Sschwarze 	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
380ca0ce676Sschwarze 		/*
381ca0ce676Sschwarze 		 * If we need a newline, print it now and start afresh.
382ca0ce676Sschwarze 		 */
38354cad943Sschwarze 		if (MMAN_PP & outflags) {
3849c576c3dSschwarze 			if (MMAN_sp & outflags) {
3859c576c3dSschwarze 				if (MMAN_PD & outflags) {
3869c576c3dSschwarze 					printf("\n.PD");
3879c576c3dSschwarze 					outflags &= ~MMAN_PD;
3889c576c3dSschwarze 				}
3899c576c3dSschwarze 			} else if ( ! (MMAN_PD & outflags)) {
3909c576c3dSschwarze 				printf("\n.PD 0");
3919c576c3dSschwarze 				outflags |= MMAN_PD;
3929c576c3dSschwarze 			}
39354cad943Sschwarze 			printf("\n.PP\n");
39454cad943Sschwarze 		} else if (MMAN_sp & outflags)
39528ee9174Sschwarze 			printf("\n.sp\n");
39628ee9174Sschwarze 		else if (MMAN_br & outflags)
39728ee9174Sschwarze 			printf("\n.br\n");
39828ee9174Sschwarze 		else if (MMAN_nl & outflags)
39975d4d0e5Sschwarze 			putchar('\n');
40054cad943Sschwarze 		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
40180a412c7Sschwarze 		if (1 == TPremain)
40280a412c7Sschwarze 			printf(".br\n");
40380a412c7Sschwarze 		TPremain = 0;
40480a412c7Sschwarze 	} else if (MMAN_spc & outflags) {
405ca0ce676Sschwarze 		/*
406d4adcee8Sschwarze 		 * If we need a space, only print it if
407d4adcee8Sschwarze 		 * (1) it is forced by `No' or
408d4adcee8Sschwarze 		 * (2) what follows is not terminating punctuation or
409d4adcee8Sschwarze 		 * (3) what follows is longer than one character.
410ca0ce676Sschwarze 		 */
41180a412c7Sschwarze 		if (MMAN_spc_force & outflags || '\0' == s[0] ||
412d4adcee8Sschwarze 		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
413f54686a1Sschwarze 			if (MMAN_Bk & outflags &&
414f54686a1Sschwarze 			    ! (MMAN_Bk_susp & outflags))
4158b4da3beSschwarze 				putchar('\\');
41675d4d0e5Sschwarze 			putchar(' ');
41780a412c7Sschwarze 			if (TPremain)
41880a412c7Sschwarze 				TPremain--;
41980a412c7Sschwarze 		}
4208b4da3beSschwarze 	}
421ca0ce676Sschwarze 
422ca0ce676Sschwarze 	/*
423ca0ce676Sschwarze 	 * Reassign needing space if we're not following opening
424ca0ce676Sschwarze 	 * punctuation.
425ca0ce676Sschwarze 	 */
42680a412c7Sschwarze 	if (MMAN_Sm & outflags && ('\0' == s[0] ||
42780a412c7Sschwarze 	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
42851c88ffdSschwarze 		outflags |= MMAN_spc;
42951c88ffdSschwarze 	else
43051c88ffdSschwarze 		outflags &= ~MMAN_spc;
431f54686a1Sschwarze 	outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
432ca0ce676Sschwarze 
43375d4d0e5Sschwarze 	for ( ; *s; s++) {
43475d4d0e5Sschwarze 		switch (*s) {
43549aff9f8Sschwarze 		case ASCII_NBRSP:
4360fc7a91fSmillert 			printf("\\ ");
43775d4d0e5Sschwarze 			break;
43849aff9f8Sschwarze 		case ASCII_HYPH:
43975d4d0e5Sschwarze 			putchar('-');
44075d4d0e5Sschwarze 			break;
44149aff9f8Sschwarze 		case ASCII_BREAK:
4421281a50cSschwarze 			printf("\\:");
4431281a50cSschwarze 			break;
44449aff9f8Sschwarze 		case ' ':
4451192b926Sschwarze 			if (MMAN_nbrword & outflags) {
4461192b926Sschwarze 				printf("\\ ");
4471192b926Sschwarze 				break;
4481192b926Sschwarze 			}
4491192b926Sschwarze 			/* FALLTHROUGH */
45075d4d0e5Sschwarze 		default:
451ca0ce676Sschwarze 			putchar((unsigned char)*s);
45275d4d0e5Sschwarze 			break;
45375d4d0e5Sschwarze 		}
45480a412c7Sschwarze 		if (TPremain)
45580a412c7Sschwarze 			TPremain--;
45675d4d0e5Sschwarze 	}
4571192b926Sschwarze 	outflags &= ~MMAN_nbrword;
45875d4d0e5Sschwarze }
45975d4d0e5Sschwarze 
46087accd8cSschwarze static void
46154cad943Sschwarze print_line(const char *s, int newflags)
46254cad943Sschwarze {
46354cad943Sschwarze 
46454cad943Sschwarze 	outflags |= MMAN_nl;
46554cad943Sschwarze 	print_word(s);
46654cad943Sschwarze 	outflags |= newflags;
46754cad943Sschwarze }
46854cad943Sschwarze 
46954cad943Sschwarze static void
47054cad943Sschwarze print_block(const char *s, int newflags)
47154cad943Sschwarze {
47254cad943Sschwarze 
47354cad943Sschwarze 	outflags &= ~MMAN_PP;
4749c576c3dSschwarze 	if (MMAN_sp & outflags) {
47554cad943Sschwarze 		outflags &= ~(MMAN_sp | MMAN_br);
4769c576c3dSschwarze 		if (MMAN_PD & outflags) {
4779c576c3dSschwarze 			print_line(".PD", 0);
4789c576c3dSschwarze 			outflags &= ~MMAN_PD;
4799c576c3dSschwarze 		}
480f54686a1Sschwarze 	} else if (! (MMAN_PD & outflags))
481f54686a1Sschwarze 		print_line(".PD 0", MMAN_PD);
48254cad943Sschwarze 	outflags |= MMAN_nl;
48354cad943Sschwarze 	print_word(s);
484f54686a1Sschwarze 	outflags |= MMAN_Bk_susp | newflags;
48554cad943Sschwarze }
48654cad943Sschwarze 
48754cad943Sschwarze static void
48890d52a15Sschwarze print_offs(const char *v, int keywords)
48987accd8cSschwarze {
49087accd8cSschwarze 	char		  buf[24];
49187accd8cSschwarze 	struct roffsu	  su;
492ecd22486Sschwarze 	const char	 *end;
49325da2733Sschwarze 	int		  sz;
49487accd8cSschwarze 
495f7135bb7Sschwarze 	print_line(".RS", MMAN_Bk_susp);
496f7135bb7Sschwarze 
4975ee03fc6Sschwarze 	/* Convert v into a number (of characters). */
49890d52a15Sschwarze 	if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
49987accd8cSschwarze 		sz = 0;
50090d52a15Sschwarze 	else if (keywords && !strcmp(v, "indent"))
50187accd8cSschwarze 		sz = 6;
50290d52a15Sschwarze 	else if (keywords && !strcmp(v, "indent-two"))
50387accd8cSschwarze 		sz = 12;
504ecd22486Sschwarze 	else {
505ecd22486Sschwarze 		end = a2roffsu(v, &su, SCALE_EN);
506ecd22486Sschwarze 		if (end == NULL || *end != '\0')
507ecd22486Sschwarze 			sz = man_strlen(v);
508ecd22486Sschwarze 		else if (SCALE_EN == su.unit)
5095ee03fc6Sschwarze 			sz = su.scale;
5105ee03fc6Sschwarze 		else {
5115ee03fc6Sschwarze 			/*
5125ee03fc6Sschwarze 			 * XXX
5135ee03fc6Sschwarze 			 * If we are inside an enclosing list,
5145ee03fc6Sschwarze 			 * there is no easy way to add the two
5155ee03fc6Sschwarze 			 * indentations because they are provided
5165ee03fc6Sschwarze 			 * in terms of different units.
5175ee03fc6Sschwarze 			 */
51851c88ffdSschwarze 			print_word(v);
519f7135bb7Sschwarze 			outflags |= MMAN_nl;
52087accd8cSschwarze 			return;
5215ee03fc6Sschwarze 		}
522ecd22486Sschwarze 	}
52387accd8cSschwarze 
5245ee03fc6Sschwarze 	/*
5255ee03fc6Sschwarze 	 * We are inside an enclosing list.
5265ee03fc6Sschwarze 	 * Add the two indentations.
5275ee03fc6Sschwarze 	 */
5285ee03fc6Sschwarze 	if (Bl_stack_len)
5295ee03fc6Sschwarze 		sz += Bl_stack[Bl_stack_len - 1];
5305ee03fc6Sschwarze 
53125da2733Sschwarze 	(void)snprintf(buf, sizeof(buf), "%dn", sz);
53251c88ffdSschwarze 	print_word(buf);
533f7135bb7Sschwarze 	outflags |= MMAN_nl;
53487accd8cSschwarze }
53587accd8cSschwarze 
536a14e0b20Sschwarze /*
537a14e0b20Sschwarze  * Set up the indentation for a list item; used from pre_it().
538a14e0b20Sschwarze  */
5394f4f7972Sschwarze static void
5403a0d07afSschwarze print_width(const struct mdoc_bl *bl, const struct roff_node *child)
5415eced068Sschwarze {
5425eced068Sschwarze 	char		  buf[24];
5435eced068Sschwarze 	struct roffsu	  su;
544ecd22486Sschwarze 	const char	 *end;
54525da2733Sschwarze 	int		  numeric, remain, sz, chsz;
54608765786Sschwarze 
54780a412c7Sschwarze 	numeric = 1;
54880a412c7Sschwarze 	remain = 0;
5495ee03fc6Sschwarze 
55025da2733Sschwarze 	/* Convert the width into a number (of characters). */
55125da2733Sschwarze 	if (bl->width == NULL)
55225da2733Sschwarze 		sz = (bl->type == LIST_hang) ? 6 : 0;
553ecd22486Sschwarze 	else {
554ecd22486Sschwarze 		end = a2roffsu(bl->width, &su, SCALE_MAX);
555ecd22486Sschwarze 		if (end == NULL || *end != '\0')
556ecd22486Sschwarze 			sz = man_strlen(bl->width);
557ecd22486Sschwarze 		else if (SCALE_EN == su.unit)
5585eced068Sschwarze 			sz = su.scale;
5595eced068Sschwarze 		else {
56080a412c7Sschwarze 			sz = 0;
56180a412c7Sschwarze 			numeric = 0;
5625eced068Sschwarze 		}
563ecd22486Sschwarze 	}
5645eced068Sschwarze 
56580a412c7Sschwarze 	/* XXX Rough estimation, might have multiple parts. */
56625da2733Sschwarze 	if (bl->type == LIST_enum)
56725da2733Sschwarze 		chsz = (bl->count > 8) + 1;
568d1982c71Sschwarze 	else if (child != NULL && child->type == ROFFT_TEXT)
5699f97fb19Sschwarze 		chsz = man_strlen(child->string);
57025da2733Sschwarze 	else
57125da2733Sschwarze 		chsz = 0;
57280a412c7Sschwarze 
573a14e0b20Sschwarze 	/* Maybe we are inside an enclosing list? */
574a14e0b20Sschwarze 	mid_it();
5755ee03fc6Sschwarze 
5765ee03fc6Sschwarze 	/*
5775ee03fc6Sschwarze 	 * Save our own indentation,
5785ee03fc6Sschwarze 	 * such that child lists can use it.
5795ee03fc6Sschwarze 	 */
5805ee03fc6Sschwarze 	Bl_stack[Bl_stack_len++] = sz + 2;
5815ee03fc6Sschwarze 
5825ee03fc6Sschwarze 	/* Set up the current list. */
58325da2733Sschwarze 	if (chsz > sz && bl->type != LIST_tag)
58428ac7daaSschwarze 		print_block(".HP", MMAN_spc);
58580a412c7Sschwarze 	else {
58628ac7daaSschwarze 		print_block(".TP", MMAN_spc);
58780a412c7Sschwarze 		remain = sz + 2;
58880a412c7Sschwarze 	}
58980a412c7Sschwarze 	if (numeric) {
59025da2733Sschwarze 		(void)snprintf(buf, sizeof(buf), "%dn", sz + 2);
5915eced068Sschwarze 		print_word(buf);
59280a412c7Sschwarze 	} else
59325da2733Sschwarze 		print_word(bl->width);
59480a412c7Sschwarze 	TPremain = remain;
5955eced068Sschwarze }
5965eced068Sschwarze 
5974f4f7972Sschwarze static void
5985eced068Sschwarze print_count(int *count)
5995eced068Sschwarze {
60047813146Sschwarze 	char		  buf[24];
6015eced068Sschwarze 
60225da2733Sschwarze 	(void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
6035eced068Sschwarze 	print_word(buf);
6045eced068Sschwarze }
6055eced068Sschwarze 
6065eced068Sschwarze void
6076b86842eSschwarze man_mdoc(void *arg, const struct roff_meta *mdoc)
60875d4d0e5Sschwarze {
6093a0d07afSschwarze 	struct roff_node *n;
61075d4d0e5Sschwarze 
6114c293873Sschwarze 	printf(".\\\" Automatically generated from an mdoc input file."
6124c293873Sschwarze 	    "  Do not edit.\n");
6134c293873Sschwarze 	for (n = mdoc->first->child; n != NULL; n = n->next) {
6144c293873Sschwarze 		if (n->type != ROFFT_COMMENT)
6154c293873Sschwarze 			break;
6164c293873Sschwarze 		printf(".\\\"%s\n", n->string);
6174c293873Sschwarze 	}
6184c293873Sschwarze 
619ac65dd21Smillert 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
6206b86842eSschwarze 	    mdoc->title, (mdoc->msec == NULL ? "" : mdoc->msec),
6216b86842eSschwarze 	    mdoc->date, mdoc->os, mdoc->vol);
62275d4d0e5Sschwarze 
623ac65dd21Smillert 	/* Disable hyphenation and if nroff, disable justification. */
624ac65dd21Smillert 	printf(".nh\n.if n .ad l");
625ac65dd21Smillert 
62651c88ffdSschwarze 	outflags = MMAN_nl | MMAN_Sm;
627388939aeSschwarze 	if (0 == fontqueue.size) {
628388939aeSschwarze 		fontqueue.size = 8;
629388939aeSschwarze 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
630388939aeSschwarze 		*fontqueue.tail = 'R';
631388939aeSschwarze 	}
6324c293873Sschwarze 	for (; n != NULL; n = n->next)
6336b86842eSschwarze 		print_node(mdoc, n);
634e6ab379cSschwarze 	putchar('\n');
63575d4d0e5Sschwarze }
63675d4d0e5Sschwarze 
63775d4d0e5Sschwarze static void
63875d4d0e5Sschwarze print_node(DECL_ARGS)
63975d4d0e5Sschwarze {
64016fe0cfcSschwarze 	const struct mdoc_man_act	*act;
6413a0d07afSschwarze 	struct roff_node		*sub;
64275d4d0e5Sschwarze 	int				 cond, do_sub;
64375d4d0e5Sschwarze 
64443808411Sschwarze 	if (n->flags & NODE_NOPRT)
64543808411Sschwarze 		return;
64643808411Sschwarze 
647ca0ce676Sschwarze 	/*
648ca0ce676Sschwarze 	 * Break the line if we were parsed subsequent the current node.
649ca0ce676Sschwarze 	 * This makes the page structure be more consistent.
650ca0ce676Sschwarze 	 */
6517ebbefbeSschwarze 	if (outflags & MMAN_spc &&
6527ebbefbeSschwarze 	    n->flags & NODE_LINE &&
6537ebbefbeSschwarze 	    !roff_node_transparent(n))
65451c88ffdSschwarze 		outflags |= MMAN_nl;
65575d4d0e5Sschwarze 
656ca0ce676Sschwarze 	act = NULL;
65775d4d0e5Sschwarze 	cond = 0;
65875d4d0e5Sschwarze 	do_sub = 1;
659c4b0939cSschwarze 	n->flags &= ~NODE_ENDED;
660ca0ce676Sschwarze 
661c96b4f2bSschwarze 	switch (n->type) {
662c96b4f2bSschwarze 	case ROFFT_EQN:
663c96b4f2bSschwarze 	case ROFFT_TBL:
664c96b4f2bSschwarze 		mandoc_msg(n->type == ROFFT_EQN ? MANDOCERR_EQN_TMAN :
665c96b4f2bSschwarze 		    MANDOCERR_TBL_TMAN, n->line, n->pos, NULL);
666c96b4f2bSschwarze 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
667c96b4f2bSschwarze 		print_word("The");
668c96b4f2bSschwarze 		print_line(".B \\-T man", MMAN_nl);
669c96b4f2bSschwarze 		print_word("output mode does not support");
670c96b4f2bSschwarze 		print_word(n->type == ROFFT_EQN ? "eqn(7)" : "tbl(7)");
671c96b4f2bSschwarze 		print_word("input.");
672c96b4f2bSschwarze 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
673c96b4f2bSschwarze 		return;
674c96b4f2bSschwarze 	case ROFFT_TEXT:
675ca0ce676Sschwarze 		/*
676ca0ce676Sschwarze 		 * Make sure that we don't happen to start with a
677ca0ce676Sschwarze 		 * control character at the start of a line.
678ca0ce676Sschwarze 		 */
67949aff9f8Sschwarze 		if (MMAN_nl & outflags &&
68049aff9f8Sschwarze 		    ('.' == *n->string || '\'' == *n->string)) {
68180a412c7Sschwarze 			print_word("");
68280a412c7Sschwarze 			printf("\\&");
68351c88ffdSschwarze 			outflags &= ~MMAN_spc;
684e6ab379cSschwarze 		}
6858ccddcd3Sschwarze 		if (n->flags & NODE_DELIMC)
6868ccddcd3Sschwarze 			outflags &= ~(MMAN_spc | MMAN_spc_force);
6878ccddcd3Sschwarze 		else if (outflags & MMAN_Sm)
6886f9818f6Sschwarze 			outflags |= MMAN_spc_force;
68951c88ffdSschwarze 		print_word(n->string);
6908ccddcd3Sschwarze 		if (n->flags & NODE_DELIMO)
6918ccddcd3Sschwarze 			outflags &= ~(MMAN_spc | MMAN_spc_force);
6928ccddcd3Sschwarze 		else if (outflags & MMAN_Sm)
6936f9818f6Sschwarze 			outflags |= MMAN_spc;
694c96b4f2bSschwarze 		break;
695c96b4f2bSschwarze 	default:
696c96b4f2bSschwarze 		if (n->tok < ROFF_MAX) {
69716fe0cfcSschwarze 			(*roff_man_acts[n->tok])(meta, n);
698644b390bSschwarze 			return;
699c96b4f2bSschwarze 		}
70016fe0cfcSschwarze 		act = mdoc_man_act(n->tok);
7017d18a139Sschwarze 		cond = act->cond == NULL || (*act->cond)(meta, n);
70230e5ee06Sschwarze 		if (cond && act->pre != NULL &&
70330e5ee06Sschwarze 		    (n->end == ENDBODY_NOT || n->child != NULL))
7047ead8a4eSschwarze 			do_sub = (*act->pre)(meta, n);
705c96b4f2bSschwarze 		break;
70675d4d0e5Sschwarze 	}
70775d4d0e5Sschwarze 
708ca0ce676Sschwarze 	/*
709ca0ce676Sschwarze 	 * Conditionally run all child nodes.
710ca0ce676Sschwarze 	 * Note that this iterates over children instead of using
711ca0ce676Sschwarze 	 * recursion.  This prevents unnecessary depth in the stack.
712ca0ce676Sschwarze 	 */
71375d4d0e5Sschwarze 	if (do_sub)
71475d4d0e5Sschwarze 		for (sub = n->child; sub; sub = sub->next)
7157ead8a4eSschwarze 			print_node(meta, sub);
71675d4d0e5Sschwarze 
717ca0ce676Sschwarze 	/*
718ca0ce676Sschwarze 	 * Lastly, conditionally run the post-node handler.
719ca0ce676Sschwarze 	 */
720c4b0939cSschwarze 	if (NODE_ENDED & n->flags)
721e63c2667Sschwarze 		return;
722e63c2667Sschwarze 
72375d4d0e5Sschwarze 	if (cond && act->post)
7247ead8a4eSschwarze 		(*act->post)(meta, n);
725e63c2667Sschwarze 
726e63c2667Sschwarze 	if (ENDBODY_NOT != n->end)
727c4b0939cSschwarze 		n->body->flags |= NODE_ENDED;
72875d4d0e5Sschwarze }
72975d4d0e5Sschwarze 
73075d4d0e5Sschwarze static int
73175d4d0e5Sschwarze cond_head(DECL_ARGS)
73275d4d0e5Sschwarze {
733ca0ce676Sschwarze 
734526e306bSschwarze 	return n->type == ROFFT_HEAD;
73575d4d0e5Sschwarze }
73675d4d0e5Sschwarze 
73775d4d0e5Sschwarze static int
73875d4d0e5Sschwarze cond_body(DECL_ARGS)
73975d4d0e5Sschwarze {
740ca0ce676Sschwarze 
741526e306bSschwarze 	return n->type == ROFFT_BODY;
74275d4d0e5Sschwarze }
74375d4d0e5Sschwarze 
74475d4d0e5Sschwarze static int
7457c539ecbSschwarze pre_abort(DECL_ARGS)
7467c539ecbSschwarze {
7477c539ecbSschwarze 	abort();
7487c539ecbSschwarze }
7497c539ecbSschwarze 
7507c539ecbSschwarze static int
75175d4d0e5Sschwarze pre_enc(DECL_ARGS)
75275d4d0e5Sschwarze {
75375d4d0e5Sschwarze 	const char	*prefix;
75475d4d0e5Sschwarze 
75516fe0cfcSschwarze 	prefix = mdoc_man_act(n->tok)->prefix;
75675d4d0e5Sschwarze 	if (NULL == prefix)
757526e306bSschwarze 		return 1;
75851c88ffdSschwarze 	print_word(prefix);
75951c88ffdSschwarze 	outflags &= ~MMAN_spc;
760526e306bSschwarze 	return 1;
76175d4d0e5Sschwarze }
76275d4d0e5Sschwarze 
76375d4d0e5Sschwarze static void
76475d4d0e5Sschwarze post_enc(DECL_ARGS)
76575d4d0e5Sschwarze {
76675d4d0e5Sschwarze 	const char *suffix;
76775d4d0e5Sschwarze 
76816fe0cfcSschwarze 	suffix = mdoc_man_act(n->tok)->suffix;
76975d4d0e5Sschwarze 	if (NULL == suffix)
77075d4d0e5Sschwarze 		return;
771e63c2667Sschwarze 	outflags &= ~(MMAN_spc | MMAN_nl);
77251c88ffdSschwarze 	print_word(suffix);
773388939aeSschwarze }
774388939aeSschwarze 
775e214f641Sschwarze static int
776e214f641Sschwarze pre_ex(DECL_ARGS)
777e214f641Sschwarze {
778e214f641Sschwarze 	outflags |= MMAN_br | MMAN_nl;
7798ccddcd3Sschwarze 	return 1;
780e214f641Sschwarze }
781e214f641Sschwarze 
782388939aeSschwarze static void
783388939aeSschwarze post_font(DECL_ARGS)
784388939aeSschwarze {
785388939aeSschwarze 
786388939aeSschwarze 	font_pop();
78775d4d0e5Sschwarze }
78875d4d0e5Sschwarze 
78975d4d0e5Sschwarze static void
79075d4d0e5Sschwarze post_percent(DECL_ARGS)
79175d4d0e5Sschwarze {
7927ebbefbeSschwarze 	struct roff_node *np, *nn, *nnn;
79375d4d0e5Sschwarze 
79416fe0cfcSschwarze 	if (mdoc_man_act(n->tok)->pre == pre_em)
795e3ceda8aSschwarze 		font_pop();
7967ebbefbeSschwarze 
797*723822f6Sschwarze 	if (n->parent == NULL || n->parent->tok != MDOC_Rs)
798*723822f6Sschwarze 		return;
799*723822f6Sschwarze 
8007ebbefbeSschwarze 	if ((nn = roff_node_next(n)) != NULL) {
8017ebbefbeSschwarze 		np = roff_node_prev(n);
8027ebbefbeSschwarze 		nnn = nn == NULL ? NULL : roff_node_next(nn);
8037ebbefbeSschwarze 		if (nn->tok != n->tok ||
8047ebbefbeSschwarze 		    (np != NULL && np->tok == n->tok) ||
8057ebbefbeSschwarze 		    (nnn != NULL && nnn->tok == n->tok))
80651c88ffdSschwarze 			print_word(",");
8077ebbefbeSschwarze 		if (nn->tok == n->tok &&
8087ebbefbeSschwarze 		    (nnn == NULL || nnn->tok != n->tok))
809e3ceda8aSschwarze 			print_word("and");
810e3ceda8aSschwarze 	} else {
81151c88ffdSschwarze 		print_word(".");
81251c88ffdSschwarze 		outflags |= MMAN_nl;
81375d4d0e5Sschwarze 	}
81475d4d0e5Sschwarze }
81575d4d0e5Sschwarze 
816e3ceda8aSschwarze static int
817e3ceda8aSschwarze pre__t(DECL_ARGS)
818e3ceda8aSschwarze {
819e3ceda8aSschwarze 
8206b7d675aSschwarze 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) {
8211aeba4ffSschwarze 		print_word("\\(lq");
822e3ceda8aSschwarze 		outflags &= ~MMAN_spc;
823e3ceda8aSschwarze 	} else
824e3ceda8aSschwarze 		font_push('I');
825526e306bSschwarze 	return 1;
826e3ceda8aSschwarze }
827e3ceda8aSschwarze 
828e3ceda8aSschwarze static void
829e3ceda8aSschwarze post__t(DECL_ARGS)
830e3ceda8aSschwarze {
831e3ceda8aSschwarze 
8326b7d675aSschwarze 	if (n->parent->tok  == MDOC_Rs && n->parent->norm->Rs.quote_T) {
833e3ceda8aSschwarze 		outflags &= ~MMAN_spc;
8341aeba4ffSschwarze 		print_word("\\(rq");
835e3ceda8aSschwarze 	} else
836e3ceda8aSschwarze 		font_pop();
8377ead8a4eSschwarze 	post_percent(meta, n);
838e3ceda8aSschwarze }
839e3ceda8aSschwarze 
840ca0ce676Sschwarze /*
841ca0ce676Sschwarze  * Print before a section header.
842ca0ce676Sschwarze  */
84375d4d0e5Sschwarze static int
844e6ab379cSschwarze pre_sect(DECL_ARGS)
845e6ab379cSschwarze {
846e6ab379cSschwarze 
847d1982c71Sschwarze 	if (n->type == ROFFT_HEAD) {
84854cad943Sschwarze 		outflags |= MMAN_sp;
84916fe0cfcSschwarze 		print_block(mdoc_man_act(n->tok)->prefix, 0);
85080a412c7Sschwarze 		print_word("");
85180a412c7Sschwarze 		putchar('\"');
85251c88ffdSschwarze 		outflags &= ~MMAN_spc;
853f54686a1Sschwarze 	}
854526e306bSschwarze 	return 1;
855e6ab379cSschwarze }
856e6ab379cSschwarze 
857ca0ce676Sschwarze /*
858ca0ce676Sschwarze  * Print subsequent a section header.
859ca0ce676Sschwarze  */
860e6ab379cSschwarze static void
861e6ab379cSschwarze post_sect(DECL_ARGS)
862e6ab379cSschwarze {
863e6ab379cSschwarze 
864d1982c71Sschwarze 	if (n->type != ROFFT_HEAD)
865e6ab379cSschwarze 		return;
86651c88ffdSschwarze 	outflags &= ~MMAN_spc;
86780a412c7Sschwarze 	print_word("");
86880a412c7Sschwarze 	putchar('\"');
86951c88ffdSschwarze 	outflags |= MMAN_nl;
8708ba5d246Sschwarze 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
8718ba5d246Sschwarze 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
8728ba5d246Sschwarze }
8738ba5d246Sschwarze 
874fb6b7277Sschwarze /* See mdoc_term.c, synopsis_pre() for comments. */
875fb6b7277Sschwarze static void
8767ebbefbeSschwarze pre_syn(struct roff_node *n)
877fb6b7277Sschwarze {
8787ebbefbeSschwarze 	struct roff_node *np;
879fb6b7277Sschwarze 
8807ebbefbeSschwarze 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
8817ebbefbeSschwarze 	    (np = roff_node_prev(n)) == NULL)
882fb6b7277Sschwarze 		return;
883fb6b7277Sschwarze 
8847ebbefbeSschwarze 	if (np->tok == n->tok &&
885fb6b7277Sschwarze 	    MDOC_Ft != n->tok &&
886fb6b7277Sschwarze 	    MDOC_Fo != n->tok &&
887fb6b7277Sschwarze 	    MDOC_Fn != n->tok) {
888fb6b7277Sschwarze 		outflags |= MMAN_br;
889fb6b7277Sschwarze 		return;
890fb6b7277Sschwarze 	}
891fb6b7277Sschwarze 
8927ebbefbeSschwarze 	switch (np->tok) {
89349aff9f8Sschwarze 	case MDOC_Fd:
89449aff9f8Sschwarze 	case MDOC_Fn:
89549aff9f8Sschwarze 	case MDOC_Fo:
89649aff9f8Sschwarze 	case MDOC_In:
89749aff9f8Sschwarze 	case MDOC_Vt:
898fb6b7277Sschwarze 		outflags |= MMAN_sp;
899fb6b7277Sschwarze 		break;
90049aff9f8Sschwarze 	case MDOC_Ft:
901fb6b7277Sschwarze 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
902fb6b7277Sschwarze 			outflags |= MMAN_sp;
903fb6b7277Sschwarze 			break;
904fb6b7277Sschwarze 		}
905fb6b7277Sschwarze 		/* FALLTHROUGH */
906fb6b7277Sschwarze 	default:
907fb6b7277Sschwarze 		outflags |= MMAN_br;
908fb6b7277Sschwarze 		break;
909fb6b7277Sschwarze 	}
910fb6b7277Sschwarze }
911fb6b7277Sschwarze 
9128ba5d246Sschwarze static int
9138ba5d246Sschwarze pre_an(DECL_ARGS)
9148ba5d246Sschwarze {
9158ba5d246Sschwarze 
9168ba5d246Sschwarze 	switch (n->norm->An.auth) {
91749aff9f8Sschwarze 	case AUTH_split:
9188ba5d246Sschwarze 		outflags &= ~MMAN_An_nosplit;
9198ba5d246Sschwarze 		outflags |= MMAN_An_split;
920526e306bSschwarze 		return 0;
92149aff9f8Sschwarze 	case AUTH_nosplit:
9228ba5d246Sschwarze 		outflags &= ~MMAN_An_split;
9238ba5d246Sschwarze 		outflags |= MMAN_An_nosplit;
924526e306bSschwarze 		return 0;
9258ba5d246Sschwarze 	default:
9268ba5d246Sschwarze 		if (MMAN_An_split & outflags)
9278ba5d246Sschwarze 			outflags |= MMAN_br;
9288ba5d246Sschwarze 		else if (SEC_AUTHORS == n->sec &&
9298ba5d246Sschwarze 		    ! (MMAN_An_nosplit & outflags))
9308ba5d246Sschwarze 			outflags |= MMAN_An_split;
931526e306bSschwarze 		return 1;
9328ba5d246Sschwarze 	}
933e6ab379cSschwarze }
934e6ab379cSschwarze 
935e6ab379cSschwarze static int
936e6ab379cSschwarze pre_ap(DECL_ARGS)
937e6ab379cSschwarze {
938e6ab379cSschwarze 
93951c88ffdSschwarze 	outflags &= ~MMAN_spc;
94051c88ffdSschwarze 	print_word("'");
94151c88ffdSschwarze 	outflags &= ~MMAN_spc;
942526e306bSschwarze 	return 0;
943e6ab379cSschwarze }
944e6ab379cSschwarze 
945e6ab379cSschwarze static int
94627d19a00Sschwarze pre_aq(DECL_ARGS)
94727d19a00Sschwarze {
94827d19a00Sschwarze 
94930e5ee06Sschwarze 	print_word(n->child != NULL && n->child->next == NULL &&
950ba61527cSschwarze 	    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
95127d19a00Sschwarze 	outflags &= ~MMAN_spc;
952526e306bSschwarze 	return 1;
95327d19a00Sschwarze }
95427d19a00Sschwarze 
95527d19a00Sschwarze static void
95627d19a00Sschwarze post_aq(DECL_ARGS)
95727d19a00Sschwarze {
95827d19a00Sschwarze 
95927d19a00Sschwarze 	outflags &= ~(MMAN_spc | MMAN_nl);
96030e5ee06Sschwarze 	print_word(n->child != NULL && n->child->next == NULL &&
961ba61527cSschwarze 	    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
96227d19a00Sschwarze }
96327d19a00Sschwarze 
96427d19a00Sschwarze static int
965e6ab379cSschwarze pre_bd(DECL_ARGS)
966e6ab379cSschwarze {
96754cad943Sschwarze 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
9687ebbefbeSschwarze 	if (n->norm->Bd.type == DISP_unfilled ||
9697ebbefbeSschwarze 	    n->norm->Bd.type == DISP_literal)
97054cad943Sschwarze 		print_line(".nf", 0);
9717ebbefbeSschwarze 	if (n->norm->Bd.comp == 0 && roff_node_prev(n->parent) != NULL)
97254cad943Sschwarze 		outflags |= MMAN_sp;
97390d52a15Sschwarze 	print_offs(n->norm->Bd.offs, 1);
974526e306bSschwarze 	return 1;
975e6ab379cSschwarze }
976e6ab379cSschwarze 
977e6ab379cSschwarze static void
978e6ab379cSschwarze post_bd(DECL_ARGS)
979e6ab379cSschwarze {
980b7fc66beSschwarze 	enum roff_tok	 bef, now;
981e6ab379cSschwarze 
9825ee03fc6Sschwarze 	/* Close out this display. */
98354cad943Sschwarze 	print_line(".RE", MMAN_nl);
984b7fc66beSschwarze 	bef = n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
985b7fc66beSschwarze 	if (n->last == NULL)
986b7fc66beSschwarze 		now = n->norm->Bd.type == DISP_unfilled ||
987b7fc66beSschwarze 		    n->norm->Bd.type == DISP_literal ? ROFF_nf : ROFF_fi;
988b7fc66beSschwarze 	else if (n->last->tok == ROFF_nf)
989b7fc66beSschwarze 		now = ROFF_nf;
990b7fc66beSschwarze 	else if (n->last->tok == ROFF_fi)
991b7fc66beSschwarze 		now = ROFF_fi;
992b7fc66beSschwarze 	else
993b7fc66beSschwarze 		now = n->last->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
994b7fc66beSschwarze 	if (bef != now) {
995b7fc66beSschwarze 		outflags |= MMAN_nl;
996b7fc66beSschwarze 		print_word(".");
997b7fc66beSschwarze 		outflags &= ~MMAN_spc;
998b7fc66beSschwarze 		print_word(roff_name[bef]);
999b7fc66beSschwarze 		outflags |= MMAN_nl;
1000b7fc66beSschwarze 	}
10015ee03fc6Sschwarze 
1002a14e0b20Sschwarze 	/* Maybe we are inside an enclosing list? */
10037ebbefbeSschwarze 	if (roff_node_next(n->parent) != NULL)
1004a14e0b20Sschwarze 		mid_it();
1005e6ab379cSschwarze }
1006e6ab379cSschwarze 
1007e6ab379cSschwarze static int
10080129f95fSschwarze pre_bf(DECL_ARGS)
10090129f95fSschwarze {
10100129f95fSschwarze 
10110129f95fSschwarze 	switch (n->type) {
1012d1982c71Sschwarze 	case ROFFT_BLOCK:
1013526e306bSschwarze 		return 1;
1014d1982c71Sschwarze 	case ROFFT_BODY:
10150129f95fSschwarze 		break;
10160129f95fSschwarze 	default:
1017526e306bSschwarze 		return 0;
10180129f95fSschwarze 	}
10190129f95fSschwarze 	switch (n->norm->Bf.font) {
102049aff9f8Sschwarze 	case FONT_Em:
10210129f95fSschwarze 		font_push('I');
10220129f95fSschwarze 		break;
102349aff9f8Sschwarze 	case FONT_Sy:
10240129f95fSschwarze 		font_push('B');
10250129f95fSschwarze 		break;
10260129f95fSschwarze 	default:
10270129f95fSschwarze 		font_push('R');
10280129f95fSschwarze 		break;
10290129f95fSschwarze 	}
1030526e306bSschwarze 	return 1;
10310129f95fSschwarze }
10320129f95fSschwarze 
10330129f95fSschwarze static void
10340129f95fSschwarze post_bf(DECL_ARGS)
10350129f95fSschwarze {
10360129f95fSschwarze 
1037d1982c71Sschwarze 	if (n->type == ROFFT_BODY)
10380129f95fSschwarze 		font_pop();
10390129f95fSschwarze }
10400129f95fSschwarze 
10410129f95fSschwarze static int
10428b4da3beSschwarze pre_bk(DECL_ARGS)
10438b4da3beSschwarze {
10448b4da3beSschwarze 	switch (n->type) {
1045d1982c71Sschwarze 	case ROFFT_BLOCK:
1046526e306bSschwarze 		return 1;
1047d1982c71Sschwarze 	case ROFFT_BODY:
1048f54c3aafSschwarze 	case ROFFT_ELEM:
104951c88ffdSschwarze 		outflags |= MMAN_Bk;
1050526e306bSschwarze 		return 1;
10518b4da3beSschwarze 	default:
1052526e306bSschwarze 		return 0;
10538b4da3beSschwarze 	}
10548b4da3beSschwarze }
10558b4da3beSschwarze 
10568b4da3beSschwarze static void
10578b4da3beSschwarze post_bk(DECL_ARGS)
10588b4da3beSschwarze {
1059f54c3aafSschwarze 	switch (n->type) {
1060f54c3aafSschwarze 	case ROFFT_ELEM:
1061f54c3aafSschwarze 		while ((n = n->parent) != NULL)
1062f54c3aafSschwarze 			 if (n->tok == MDOC_Bk)
1063f54c3aafSschwarze 				return;
1064f54c3aafSschwarze 		/* FALLTHROUGH */
1065f54c3aafSschwarze 	case ROFFT_BODY:
106651c88ffdSschwarze 		outflags &= ~MMAN_Bk;
1067f54c3aafSschwarze 		break;
1068f54c3aafSschwarze 	default:
1069f54c3aafSschwarze 		break;
1070f54c3aafSschwarze 	}
10718b4da3beSschwarze }
10728b4da3beSschwarze 
10738b4da3beSschwarze static int
10745eced068Sschwarze pre_bl(DECL_ARGS)
10755eced068Sschwarze {
10769ea7155cSschwarze 	size_t		 icol;
10775eced068Sschwarze 
10781b12f2b2Sschwarze 	/*
10791b12f2b2Sschwarze 	 * print_offs() will increase the -offset to account for
10801b12f2b2Sschwarze 	 * a possible enclosing .It, but any enclosed .It blocks
10811b12f2b2Sschwarze 	 * just nest and do not add up their indentation.
10821b12f2b2Sschwarze 	 */
10831b12f2b2Sschwarze 	if (n->norm->Bl.offs) {
108490d52a15Sschwarze 		print_offs(n->norm->Bl.offs, 0);
10851b12f2b2Sschwarze 		Bl_stack[Bl_stack_len++] = 0;
10861b12f2b2Sschwarze 	}
10871b12f2b2Sschwarze 
10889ea7155cSschwarze 	switch (n->norm->Bl.type) {
108949aff9f8Sschwarze 	case LIST_enum:
10905eced068Sschwarze 		n->norm->Bl.count = 0;
1091526e306bSschwarze 		return 1;
109249aff9f8Sschwarze 	case LIST_column:
10939ea7155cSschwarze 		break;
10949ea7155cSschwarze 	default:
1095526e306bSschwarze 		return 1;
10969ea7155cSschwarze 	}
10979ea7155cSschwarze 
109830e5ee06Sschwarze 	if (n->child != NULL) {
109954cad943Sschwarze 		print_line(".TS", MMAN_nl);
11009ea7155cSschwarze 		for (icol = 0; icol < n->norm->Bl.ncols; icol++)
11019ea7155cSschwarze 			print_word("l");
11029ea7155cSschwarze 		print_word(".");
110392ff8da6Sschwarze 	}
110454cad943Sschwarze 	outflags |= MMAN_nl;
1105526e306bSschwarze 	return 1;
11065eced068Sschwarze }
11075eced068Sschwarze 
11085eced068Sschwarze static void
11095eced068Sschwarze post_bl(DECL_ARGS)
11105eced068Sschwarze {
11115eced068Sschwarze 
11129ea7155cSschwarze 	switch (n->norm->Bl.type) {
111349aff9f8Sschwarze 	case LIST_column:
111430e5ee06Sschwarze 		if (n->child != NULL)
111554cad943Sschwarze 			print_line(".TE", 0);
111654cad943Sschwarze 		break;
111749aff9f8Sschwarze 	case LIST_enum:
11185eced068Sschwarze 		n->norm->Bl.count = 0;
11199ea7155cSschwarze 		break;
11209ea7155cSschwarze 	default:
11219ea7155cSschwarze 		break;
11229ea7155cSschwarze 	}
11231b12f2b2Sschwarze 
11241b12f2b2Sschwarze 	if (n->norm->Bl.offs) {
11251b12f2b2Sschwarze 		print_line(".RE", MMAN_nl);
11261b12f2b2Sschwarze 		assert(Bl_stack_len);
11271b12f2b2Sschwarze 		Bl_stack_len--;
11287ebbefbeSschwarze 		assert(Bl_stack[Bl_stack_len] == 0);
11291b12f2b2Sschwarze 	} else {
113054cad943Sschwarze 		outflags |= MMAN_PP | MMAN_nl;
113154cad943Sschwarze 		outflags &= ~(MMAN_sp | MMAN_br);
11321b12f2b2Sschwarze 	}
1133a14e0b20Sschwarze 
1134a14e0b20Sschwarze 	/* Maybe we are inside an enclosing list? */
11357ebbefbeSschwarze 	if (roff_node_next(n->parent) != NULL)
1136a14e0b20Sschwarze 		mid_it();
11375eced068Sschwarze }
11385eced068Sschwarze 
1139644b390bSschwarze static void
1140e6ab379cSschwarze pre_br(DECL_ARGS)
1141e6ab379cSschwarze {
114228ee9174Sschwarze 	outflags |= MMAN_br;
1143e6ab379cSschwarze }
1144e6ab379cSschwarze 
1145e6ab379cSschwarze static int
114675d4d0e5Sschwarze pre_dl(DECL_ARGS)
114775d4d0e5Sschwarze {
114890d52a15Sschwarze 	print_offs("6n", 0);
1149526e306bSschwarze 	return 1;
115075d4d0e5Sschwarze }
115175d4d0e5Sschwarze 
115275d4d0e5Sschwarze static void
115375d4d0e5Sschwarze post_dl(DECL_ARGS)
115475d4d0e5Sschwarze {
115554cad943Sschwarze 	print_line(".RE", MMAN_nl);
1156a14e0b20Sschwarze 
1157a14e0b20Sschwarze 	/* Maybe we are inside an enclosing list? */
11587ebbefbeSschwarze 	if (roff_node_next(n->parent) != NULL)
1159a14e0b20Sschwarze 		mid_it();
116075d4d0e5Sschwarze }
116175d4d0e5Sschwarze 
116275d4d0e5Sschwarze static int
1163388939aeSschwarze pre_em(DECL_ARGS)
1164388939aeSschwarze {
1165388939aeSschwarze 
1166388939aeSschwarze 	font_push('I');
1167526e306bSschwarze 	return 1;
1168388939aeSschwarze }
1169388939aeSschwarze 
1170551cd4a8Sschwarze static int
1171551cd4a8Sschwarze pre_en(DECL_ARGS)
1172551cd4a8Sschwarze {
1173551cd4a8Sschwarze 
1174551cd4a8Sschwarze 	if (NULL == n->norm->Es ||
1175551cd4a8Sschwarze 	    NULL == n->norm->Es->child)
1176526e306bSschwarze 		return 1;
1177551cd4a8Sschwarze 
1178551cd4a8Sschwarze 	print_word(n->norm->Es->child->string);
1179551cd4a8Sschwarze 	outflags &= ~MMAN_spc;
1180526e306bSschwarze 	return 1;
1181551cd4a8Sschwarze }
1182551cd4a8Sschwarze 
1183551cd4a8Sschwarze static void
1184551cd4a8Sschwarze post_en(DECL_ARGS)
1185551cd4a8Sschwarze {
1186551cd4a8Sschwarze 
1187551cd4a8Sschwarze 	if (NULL == n->norm->Es ||
1188551cd4a8Sschwarze 	    NULL == n->norm->Es->child ||
1189551cd4a8Sschwarze 	    NULL == n->norm->Es->child->next)
1190551cd4a8Sschwarze 		return;
1191551cd4a8Sschwarze 
1192551cd4a8Sschwarze 	outflags &= ~MMAN_spc;
1193551cd4a8Sschwarze 	print_word(n->norm->Es->child->next->string);
1194551cd4a8Sschwarze 	return;
1195551cd4a8Sschwarze }
1196551cd4a8Sschwarze 
11977d18a139Sschwarze static int
11987d18a139Sschwarze pre_eo(DECL_ARGS)
11997d18a139Sschwarze {
12007d18a139Sschwarze 
120103ed6ec9Sschwarze 	if (n->end == ENDBODY_NOT &&
120203ed6ec9Sschwarze 	    n->parent->head->child == NULL &&
120303ed6ec9Sschwarze 	    n->child != NULL &&
120403ed6ec9Sschwarze 	    n->child->end != ENDBODY_NOT)
120503ed6ec9Sschwarze 		print_word("\\&");
120603ed6ec9Sschwarze 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1207c69b5c37Sschwarze 	    n->parent->head->child != NULL && (n->child != NULL ||
1208c69b5c37Sschwarze 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
12097d18a139Sschwarze 		outflags &= ~(MMAN_spc | MMAN_nl);
1210526e306bSschwarze 	return 1;
12117d18a139Sschwarze }
12127d18a139Sschwarze 
1213c19c703eSschwarze static void
1214c19c703eSschwarze post_eo(DECL_ARGS)
1215c19c703eSschwarze {
121603ed6ec9Sschwarze 	int	 body, tail;
1217c19c703eSschwarze 
121803ed6ec9Sschwarze 	if (n->end != ENDBODY_NOT) {
121903ed6ec9Sschwarze 		outflags |= MMAN_spc;
122003ed6ec9Sschwarze 		return;
122103ed6ec9Sschwarze 	}
122203ed6ec9Sschwarze 
122303ed6ec9Sschwarze 	body = n->child != NULL || n->parent->head->child != NULL;
122403ed6ec9Sschwarze 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
122503ed6ec9Sschwarze 
122603ed6ec9Sschwarze 	if (body && tail)
1227c19c703eSschwarze 		outflags &= ~MMAN_spc;
122803ed6ec9Sschwarze 	else if ( ! (body || tail))
122903ed6ec9Sschwarze 		print_word("\\&");
123003ed6ec9Sschwarze 	else if ( ! tail)
123103ed6ec9Sschwarze 		outflags |= MMAN_spc;
1232c19c703eSschwarze }
1233c19c703eSschwarze 
1234388939aeSschwarze static int
1235e2ee3b43Sschwarze pre_fa(DECL_ARGS)
1236e2ee3b43Sschwarze {
12371074fc8aSschwarze 	int	 am_Fa;
1238e2ee3b43Sschwarze 
12391074fc8aSschwarze 	am_Fa = MDOC_Fa == n->tok;
12401074fc8aSschwarze 
12411074fc8aSschwarze 	if (am_Fa)
1242e2ee3b43Sschwarze 		n = n->child;
1243e2ee3b43Sschwarze 
1244e2ee3b43Sschwarze 	while (NULL != n) {
1245388939aeSschwarze 		font_push('I');
1246c4b0939cSschwarze 		if (am_Fa || NODE_SYNPRETTY & n->flags)
12471192b926Sschwarze 			outflags |= MMAN_nbrword;
12487ead8a4eSschwarze 		print_node(meta, n);
1249388939aeSschwarze 		font_pop();
1250e2ee3b43Sschwarze 		if (NULL != (n = n->next))
125151c88ffdSschwarze 			print_word(",");
1252e2ee3b43Sschwarze 	}
1253526e306bSschwarze 	return 0;
1254e2ee3b43Sschwarze }
1255e2ee3b43Sschwarze 
1256e2ee3b43Sschwarze static void
1257e2ee3b43Sschwarze post_fa(DECL_ARGS)
1258e2ee3b43Sschwarze {
12597ebbefbeSschwarze 	struct roff_node *nn;
1260e2ee3b43Sschwarze 
12617ebbefbeSschwarze 	if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa)
126251c88ffdSschwarze 		print_word(",");
1263e2ee3b43Sschwarze }
1264e2ee3b43Sschwarze 
1265e2ee3b43Sschwarze static int
1266d97bd30dSschwarze pre_fd(DECL_ARGS)
1267d97bd30dSschwarze {
1268d97bd30dSschwarze 	pre_syn(n);
1269d97bd30dSschwarze 	font_push('B');
1270526e306bSschwarze 	return 1;
1271d97bd30dSschwarze }
1272d97bd30dSschwarze 
1273d97bd30dSschwarze static void
1274d97bd30dSschwarze post_fd(DECL_ARGS)
1275d97bd30dSschwarze {
1276d97bd30dSschwarze 	font_pop();
1277d97bd30dSschwarze 	outflags |= MMAN_br;
1278d97bd30dSschwarze }
1279d97bd30dSschwarze 
1280d97bd30dSschwarze static int
1281388939aeSschwarze pre_fl(DECL_ARGS)
1282388939aeSschwarze {
1283388939aeSschwarze 	font_push('B');
12840e67873aSmillert 	print_word("\\-");
128530e5ee06Sschwarze 	if (n->child != NULL)
1286388939aeSschwarze 		outflags &= ~MMAN_spc;
1287526e306bSschwarze 	return 1;
1288388939aeSschwarze }
1289388939aeSschwarze 
1290388939aeSschwarze static void
1291388939aeSschwarze post_fl(DECL_ARGS)
1292388939aeSschwarze {
12937ebbefbeSschwarze 	struct roff_node *nn;
1294388939aeSschwarze 
1295388939aeSschwarze 	font_pop();
12967ebbefbeSschwarze 	if (n->child == NULL &&
12977ebbefbeSschwarze 	    ((nn = roff_node_next(n)) != NULL &&
12987ebbefbeSschwarze 	    nn->type != ROFFT_TEXT &&
12997ebbefbeSschwarze 	    (nn->flags & NODE_LINE) == 0))
1300388939aeSschwarze 		outflags &= ~MMAN_spc;
1301388939aeSschwarze }
1302388939aeSschwarze 
1303388939aeSschwarze static int
13040df06370Sschwarze pre_fn(DECL_ARGS)
13050df06370Sschwarze {
13060df06370Sschwarze 
1307fb6b7277Sschwarze 	pre_syn(n);
1308fb6b7277Sschwarze 
13090df06370Sschwarze 	n = n->child;
13100df06370Sschwarze 	if (NULL == n)
1311526e306bSschwarze 		return 0;
13120df06370Sschwarze 
1313c4b0939cSschwarze 	if (NODE_SYNPRETTY & n->flags)
13148018f0c4Sschwarze 		print_block(".HP 4n", MMAN_nl);
13158018f0c4Sschwarze 
1316388939aeSschwarze 	font_push('B');
13177ead8a4eSschwarze 	print_node(meta, n);
1318388939aeSschwarze 	font_pop();
131951c88ffdSschwarze 	outflags &= ~MMAN_spc;
1320388939aeSschwarze 	print_word("(");
132151c88ffdSschwarze 	outflags &= ~MMAN_spc;
132220649b49Sschwarze 
132320649b49Sschwarze 	n = n->next;
132420649b49Sschwarze 	if (NULL != n)
13257ead8a4eSschwarze 		pre_fa(meta, n);
1326526e306bSschwarze 	return 0;
13270df06370Sschwarze }
13280df06370Sschwarze 
13290df06370Sschwarze static void
13300df06370Sschwarze post_fn(DECL_ARGS)
13310df06370Sschwarze {
13320df06370Sschwarze 
133351c88ffdSschwarze 	print_word(")");
1334c4b0939cSschwarze 	if (NODE_SYNPRETTY & n->flags) {
133551c88ffdSschwarze 		print_word(";");
13368018f0c4Sschwarze 		outflags |= MMAN_PP;
1337e2ee3b43Sschwarze 	}
1338e2ee3b43Sschwarze }
1339e2ee3b43Sschwarze 
1340e2ee3b43Sschwarze static int
1341e2ee3b43Sschwarze pre_fo(DECL_ARGS)
1342e2ee3b43Sschwarze {
1343e2ee3b43Sschwarze 
1344e2ee3b43Sschwarze 	switch (n->type) {
1345d1982c71Sschwarze 	case ROFFT_BLOCK:
1346fb6b7277Sschwarze 		pre_syn(n);
1347fb6b7277Sschwarze 		break;
1348d1982c71Sschwarze 	case ROFFT_HEAD:
1349afcd1f03Sschwarze 		if (n->child == NULL)
1350526e306bSschwarze 			return 0;
1351c4b0939cSschwarze 		if (NODE_SYNPRETTY & n->flags)
13521074fc8aSschwarze 			print_block(".HP 4n", MMAN_nl);
1353388939aeSschwarze 		font_push('B');
1354e2ee3b43Sschwarze 		break;
1355d1982c71Sschwarze 	case ROFFT_BODY:
1356afcd1f03Sschwarze 		outflags &= ~(MMAN_spc | MMAN_nl);
135751c88ffdSschwarze 		print_word("(");
135851c88ffdSschwarze 		outflags &= ~MMAN_spc;
1359e2ee3b43Sschwarze 		break;
1360e2ee3b43Sschwarze 	default:
1361e2ee3b43Sschwarze 		break;
1362e2ee3b43Sschwarze 	}
1363526e306bSschwarze 	return 1;
1364e2ee3b43Sschwarze }
1365e2ee3b43Sschwarze 
1366e2ee3b43Sschwarze static void
1367e2ee3b43Sschwarze post_fo(DECL_ARGS)
1368e2ee3b43Sschwarze {
1369e2ee3b43Sschwarze 
1370e2ee3b43Sschwarze 	switch (n->type) {
1371d1982c71Sschwarze 	case ROFFT_HEAD:
1372afcd1f03Sschwarze 		if (n->child != NULL)
1373388939aeSschwarze 			font_pop();
1374e2ee3b43Sschwarze 		break;
1375d1982c71Sschwarze 	case ROFFT_BODY:
13767ead8a4eSschwarze 		post_fn(meta, n);
1377e2ee3b43Sschwarze 		break;
1378e2ee3b43Sschwarze 	default:
1379e2ee3b43Sschwarze 		break;
1380e2ee3b43Sschwarze 	}
13810df06370Sschwarze }
13820df06370Sschwarze 
13830df06370Sschwarze static int
1384c4d3fa85Sschwarze pre_Ft(DECL_ARGS)
1385fb6b7277Sschwarze {
1386fb6b7277Sschwarze 
1387fb6b7277Sschwarze 	pre_syn(n);
1388388939aeSschwarze 	font_push('I');
1389526e306bSschwarze 	return 1;
1390fb6b7277Sschwarze }
1391fb6b7277Sschwarze 
1392644b390bSschwarze static void
1393c4d3fa85Sschwarze pre_ft(DECL_ARGS)
1394c4d3fa85Sschwarze {
1395c4d3fa85Sschwarze 	print_line(".ft", 0);
1396c4d3fa85Sschwarze 	print_word(n->child->string);
1397c4d3fa85Sschwarze 	outflags |= MMAN_nl;
1398c4d3fa85Sschwarze }
1399c4d3fa85Sschwarze 
1400c4d3fa85Sschwarze static int
14010e0aff12Sschwarze pre_in(DECL_ARGS)
14020e0aff12Sschwarze {
14030e0aff12Sschwarze 
1404c4b0939cSschwarze 	if (NODE_SYNPRETTY & n->flags) {
1405fb6b7277Sschwarze 		pre_syn(n);
1406388939aeSschwarze 		font_push('B');
1407388939aeSschwarze 		print_word("#include <");
140851c88ffdSschwarze 		outflags &= ~MMAN_spc;
1409388939aeSschwarze 	} else {
1410388939aeSschwarze 		print_word("<");
1411388939aeSschwarze 		outflags &= ~MMAN_spc;
1412388939aeSschwarze 		font_push('I');
1413388939aeSschwarze 	}
1414526e306bSschwarze 	return 1;
14150e0aff12Sschwarze }
14160e0aff12Sschwarze 
14170e0aff12Sschwarze static void
14180e0aff12Sschwarze post_in(DECL_ARGS)
14190e0aff12Sschwarze {
14200e0aff12Sschwarze 
1421c4b0939cSschwarze 	if (NODE_SYNPRETTY & n->flags) {
1422388939aeSschwarze 		outflags &= ~MMAN_spc;
1423388939aeSschwarze 		print_word(">");
1424388939aeSschwarze 		font_pop();
142528ee9174Sschwarze 		outflags |= MMAN_br;
1426388939aeSschwarze 	} else {
1427388939aeSschwarze 		font_pop();
1428388939aeSschwarze 		outflags &= ~MMAN_spc;
1429388939aeSschwarze 		print_word(">");
1430388939aeSschwarze 	}
14310e0aff12Sschwarze }
14320e0aff12Sschwarze 
14330e0aff12Sschwarze static int
143475d4d0e5Sschwarze pre_it(DECL_ARGS)
143575d4d0e5Sschwarze {
14363a0d07afSschwarze 	const struct roff_node *bln;
143775d4d0e5Sschwarze 
14385eced068Sschwarze 	switch (n->type) {
1439d1982c71Sschwarze 	case ROFFT_HEAD:
144054cad943Sschwarze 		outflags |= MMAN_PP | MMAN_nl;
14415eced068Sschwarze 		bln = n->parent->parent;
14427ebbefbeSschwarze 		if (bln->norm->Bl.comp == 0 ||
14437ebbefbeSschwarze 		    (n->parent->prev == NULL &&
14447ebbefbeSschwarze 		     roff_node_prev(bln->parent) == NULL))
144554cad943Sschwarze 			outflags |= MMAN_sp;
144654cad943Sschwarze 		outflags &= ~MMAN_br;
1447e6ab379cSschwarze 		switch (bln->norm->Bl.type) {
144849aff9f8Sschwarze 		case LIST_item:
1449526e306bSschwarze 			return 0;
145049aff9f8Sschwarze 		case LIST_inset:
145149aff9f8Sschwarze 		case LIST_diag:
145249aff9f8Sschwarze 		case LIST_ohang:
14535eced068Sschwarze 			if (bln->norm->Bl.type == LIST_diag)
145454cad943Sschwarze 				print_line(".B \"", 0);
14555eced068Sschwarze 			else
145672ee7308Sschwarze 				print_line(".BR \\& \"", 0);
14575eced068Sschwarze 			outflags &= ~MMAN_spc;
1458526e306bSschwarze 			return 1;
145949aff9f8Sschwarze 		case LIST_bullet:
146049aff9f8Sschwarze 		case LIST_dash:
146149aff9f8Sschwarze 		case LIST_hyphen:
146225da2733Sschwarze 			print_width(&bln->norm->Bl, NULL);
146380a412c7Sschwarze 			TPremain = 0;
146451c88ffdSschwarze 			outflags |= MMAN_nl;
14655eced068Sschwarze 			font_push('B');
14665eced068Sschwarze 			if (LIST_bullet == bln->norm->Bl.type)
14677c64ba2fSschwarze 				print_word("\\(bu");
14685eced068Sschwarze 			else
14695eced068Sschwarze 				print_word("-");
14705eced068Sschwarze 			font_pop();
1471ecb10c32Sschwarze 			outflags |= MMAN_nl;
1472526e306bSschwarze 			return 0;
147349aff9f8Sschwarze 		case LIST_enum:
147425da2733Sschwarze 			print_width(&bln->norm->Bl, NULL);
147580a412c7Sschwarze 			TPremain = 0;
14765eced068Sschwarze 			outflags |= MMAN_nl;
14775eced068Sschwarze 			print_count(&bln->norm->Bl.count);
1478ecb10c32Sschwarze 			outflags |= MMAN_nl;
1479526e306bSschwarze 			return 0;
148049aff9f8Sschwarze 		case LIST_hang:
148125da2733Sschwarze 			print_width(&bln->norm->Bl, n->child);
148280a412c7Sschwarze 			TPremain = 0;
1483ecb10c32Sschwarze 			outflags |= MMAN_nl;
1484526e306bSschwarze 			return 1;
148549aff9f8Sschwarze 		case LIST_tag:
148625da2733Sschwarze 			print_width(&bln->norm->Bl, n->child);
148780a412c7Sschwarze 			putchar('\n');
148880a412c7Sschwarze 			outflags &= ~MMAN_spc;
1489526e306bSschwarze 			return 1;
1490e6ab379cSschwarze 		default:
1491526e306bSschwarze 			return 1;
1492e6ab379cSschwarze 		}
14935eced068Sschwarze 	default:
14945eced068Sschwarze 		break;
149575d4d0e5Sschwarze 	}
1496526e306bSschwarze 	return 1;
149775d4d0e5Sschwarze }
149875d4d0e5Sschwarze 
1499a14e0b20Sschwarze /*
1500a14e0b20Sschwarze  * This function is called after closing out an indented block.
1501a14e0b20Sschwarze  * If we are inside an enclosing list, restore its indentation.
1502a14e0b20Sschwarze  */
1503a14e0b20Sschwarze static void
1504a14e0b20Sschwarze mid_it(void)
1505a14e0b20Sschwarze {
1506a14e0b20Sschwarze 	char		 buf[24];
1507a14e0b20Sschwarze 
1508a14e0b20Sschwarze 	/* Nothing to do outside a list. */
1509a14e0b20Sschwarze 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1510a14e0b20Sschwarze 		return;
1511a14e0b20Sschwarze 
1512a14e0b20Sschwarze 	/* The indentation has already been set up. */
1513a14e0b20Sschwarze 	if (Bl_stack_post[Bl_stack_len - 1])
1514a14e0b20Sschwarze 		return;
1515a14e0b20Sschwarze 
1516a14e0b20Sschwarze 	/* Restore the indentation of the enclosing list. */
1517a14e0b20Sschwarze 	print_line(".RS", MMAN_Bk_susp);
151825da2733Sschwarze 	(void)snprintf(buf, sizeof(buf), "%dn",
151947813146Sschwarze 	    Bl_stack[Bl_stack_len - 1]);
1520a14e0b20Sschwarze 	print_word(buf);
1521a14e0b20Sschwarze 
1522d9a51c35Sjmc 	/* Remember to close out this .RS block later. */
1523a14e0b20Sschwarze 	Bl_stack_post[Bl_stack_len - 1] = 1;
1524a14e0b20Sschwarze }
1525a14e0b20Sschwarze 
1526835b952bSschwarze static void
15275eced068Sschwarze post_it(DECL_ARGS)
15285eced068Sschwarze {
15293a0d07afSschwarze 	const struct roff_node *bln;
15305eced068Sschwarze 
15315eced068Sschwarze 	bln = n->parent->parent;
15329ea7155cSschwarze 
15339ea7155cSschwarze 	switch (n->type) {
1534d1982c71Sschwarze 	case ROFFT_HEAD:
15355eced068Sschwarze 		switch (bln->norm->Bl.type) {
153649aff9f8Sschwarze 		case LIST_diag:
15375eced068Sschwarze 			outflags &= ~MMAN_spc;
15385eced068Sschwarze 			print_word("\\ ");
15395eced068Sschwarze 			break;
154049aff9f8Sschwarze 		case LIST_ohang:
15415eced068Sschwarze 			outflags |= MMAN_br;
15425eced068Sschwarze 			break;
15435eced068Sschwarze 		default:
15445eced068Sschwarze 			break;
15455eced068Sschwarze 		}
15469ea7155cSschwarze 		break;
1547d1982c71Sschwarze 	case ROFFT_BODY:
15485ee03fc6Sschwarze 		switch (bln->norm->Bl.type) {
154949aff9f8Sschwarze 		case LIST_bullet:
155049aff9f8Sschwarze 		case LIST_dash:
155149aff9f8Sschwarze 		case LIST_hyphen:
155249aff9f8Sschwarze 		case LIST_enum:
155349aff9f8Sschwarze 		case LIST_hang:
155449aff9f8Sschwarze 		case LIST_tag:
15555ee03fc6Sschwarze 			assert(Bl_stack_len);
15565ee03fc6Sschwarze 			Bl_stack[--Bl_stack_len] = 0;
15575ee03fc6Sschwarze 
15585ee03fc6Sschwarze 			/*
15595ee03fc6Sschwarze 			 * Our indentation had to be restored
1560a14e0b20Sschwarze 			 * after a child display or child list.
15615ee03fc6Sschwarze 			 * Close out that indentation block now.
15625ee03fc6Sschwarze 			 */
15635ee03fc6Sschwarze 			if (Bl_stack_post[Bl_stack_len]) {
15645ee03fc6Sschwarze 				print_line(".RE", MMAN_nl);
15655ee03fc6Sschwarze 				Bl_stack_post[Bl_stack_len] = 0;
15665ee03fc6Sschwarze 			}
15675ee03fc6Sschwarze 			break;
156849aff9f8Sschwarze 		case LIST_column:
15695ee03fc6Sschwarze 			if (NULL != n->next) {
15709ea7155cSschwarze 				putchar('\t');
15719ea7155cSschwarze 				outflags &= ~MMAN_spc;
15729ea7155cSschwarze 			}
15739ea7155cSschwarze 			break;
15749ea7155cSschwarze 		default:
15759ea7155cSschwarze 			break;
15765eced068Sschwarze 		}
15775ee03fc6Sschwarze 		break;
15785ee03fc6Sschwarze 	default:
15795ee03fc6Sschwarze 		break;
15805ee03fc6Sschwarze 	}
15815eced068Sschwarze }
15825eced068Sschwarze 
15835eced068Sschwarze static void
1584835b952bSschwarze post_lb(DECL_ARGS)
1585835b952bSschwarze {
1586835b952bSschwarze 
158728ee9174Sschwarze 	if (SEC_LIBRARY == n->sec)
158828ee9174Sschwarze 		outflags |= MMAN_br;
1589835b952bSschwarze }
1590835b952bSschwarze 
159175d4d0e5Sschwarze static int
1592cfd12f23Sschwarze pre_lk(DECL_ARGS)
1593cfd12f23Sschwarze {
1594eb4d0f30Sschwarze 	const struct roff_node *link, *descr, *punct;
1595cfd12f23Sschwarze 
159696aebfb3Sschwarze 	if ((link = n->child) == NULL)
1597526e306bSschwarze 		return 0;
1598cfd12f23Sschwarze 
1599eb4d0f30Sschwarze 	/* Find beginning of trailing punctuation. */
1600eb4d0f30Sschwarze 	punct = n->last;
1601eb4d0f30Sschwarze 	while (punct != link && punct->flags & NODE_DELIMC)
1602eb4d0f30Sschwarze 		punct = punct->prev;
1603eb4d0f30Sschwarze 	punct = punct->next;
1604eb4d0f30Sschwarze 
160596aebfb3Sschwarze 	/* Link text. */
1606eb4d0f30Sschwarze 	if ((descr = link->next) != NULL && descr != punct) {
1607388939aeSschwarze 		font_push('I');
1608eb4d0f30Sschwarze 		while (descr != punct) {
1609cfd12f23Sschwarze 			print_word(descr->string);
1610cfd12f23Sschwarze 			descr = descr->next;
1611cfd12f23Sschwarze 		}
1612388939aeSschwarze 		font_pop();
1613f6fe732fSschwarze 		print_word(":");
1614cfd12f23Sschwarze 	}
1615cfd12f23Sschwarze 
161696aebfb3Sschwarze 	/* Link target. */
1617388939aeSschwarze 	font_push('B');
1618cfd12f23Sschwarze 	print_word(link->string);
1619388939aeSschwarze 	font_pop();
162096aebfb3Sschwarze 
162196aebfb3Sschwarze 	/* Trailing punctuation. */
1622eb4d0f30Sschwarze 	while (punct != NULL) {
1623eb4d0f30Sschwarze 		print_word(punct->string);
1624eb4d0f30Sschwarze 		punct = punct->next;
162596aebfb3Sschwarze 	}
1626526e306bSschwarze 	return 0;
1627cfd12f23Sschwarze }
1628cfd12f23Sschwarze 
1629644b390bSschwarze static void
163011d70615Sschwarze pre_onearg(DECL_ARGS)
16315281506aSschwarze {
163211d70615Sschwarze 	outflags |= MMAN_nl;
163311d70615Sschwarze 	print_word(".");
163411d70615Sschwarze 	outflags &= ~MMAN_spc;
163511d70615Sschwarze 	print_word(roff_name[n->tok]);
1636644b390bSschwarze 	if (n->child != NULL)
1637644b390bSschwarze 		print_word(n->child->string);
1638644b390bSschwarze 	outflags |= MMAN_nl;
1639e13b4195Sschwarze 	if (n->tok == ROFF_ce)
1640e13b4195Sschwarze 		for (n = n->child->next; n != NULL; n = n->next)
1641e13b4195Sschwarze 			print_node(meta, n);
16425281506aSschwarze }
16435281506aSschwarze 
16445281506aSschwarze static int
1645388939aeSschwarze pre_li(DECL_ARGS)
1646388939aeSschwarze {
1647388939aeSschwarze 	font_push('R');
1648526e306bSschwarze 	return 1;
1649388939aeSschwarze }
1650388939aeSschwarze 
1651388939aeSschwarze static int
165275d4d0e5Sschwarze pre_nm(DECL_ARGS)
165375d4d0e5Sschwarze {
1654e0ceaf97Sschwarze 	char	*name;
165575d4d0e5Sschwarze 
16567ebbefbeSschwarze 	switch (n->type) {
16577ebbefbeSschwarze 	case ROFFT_BLOCK:
16584b1aedf8Sschwarze 		outflags |= MMAN_Bk;
1659fb6b7277Sschwarze 		pre_syn(n);
1660526e306bSschwarze 		return 1;
16617ebbefbeSschwarze 	case ROFFT_HEAD:
16627ebbefbeSschwarze 	case ROFFT_ELEM:
16637ebbefbeSschwarze 		break;
16647ebbefbeSschwarze 	default:
16657ebbefbeSschwarze 		return 1;
16667ebbefbeSschwarze 	}
166790957cf5Sschwarze 	name = n->child == NULL ? NULL : n->child->string;
16687ebbefbeSschwarze 	if (name == NULL)
1669526e306bSschwarze 		return 0;
1670d1982c71Sschwarze 	if (n->type == ROFFT_HEAD) {
16717ebbefbeSschwarze 		if (roff_node_prev(n->parent) == NULL)
1672e0ceaf97Sschwarze 			outflags |= MMAN_sp;
1673e0ceaf97Sschwarze 		print_block(".HP", 0);
16749f97fb19Sschwarze 		printf(" %dn", man_strlen(name) + 1);
1675e0ceaf97Sschwarze 		outflags |= MMAN_nl;
1676e0ceaf97Sschwarze 	}
1677388939aeSschwarze 	font_push('B');
1678526e306bSschwarze 	return 1;
167975d4d0e5Sschwarze }
168075d4d0e5Sschwarze 
168175d4d0e5Sschwarze static void
168275d4d0e5Sschwarze post_nm(DECL_ARGS)
168375d4d0e5Sschwarze {
16844b1aedf8Sschwarze 	switch (n->type) {
1685d1982c71Sschwarze 	case ROFFT_BLOCK:
16864b1aedf8Sschwarze 		outflags &= ~MMAN_Bk;
16874b1aedf8Sschwarze 		break;
1688d1982c71Sschwarze 	case ROFFT_HEAD:
1689d1982c71Sschwarze 	case ROFFT_ELEM:
169090957cf5Sschwarze 		if (n->child != NULL && n->child->string != NULL)
1691388939aeSschwarze 			font_pop();
16924b1aedf8Sschwarze 		break;
16934b1aedf8Sschwarze 	default:
16944b1aedf8Sschwarze 		break;
16954b1aedf8Sschwarze 	}
169675d4d0e5Sschwarze }
169775d4d0e5Sschwarze 
169875d4d0e5Sschwarze static int
1699d4adcee8Sschwarze pre_no(DECL_ARGS)
1700d4adcee8Sschwarze {
1701d4adcee8Sschwarze 	outflags |= MMAN_spc_force;
1702526e306bSschwarze 	return 1;
1703d4adcee8Sschwarze }
1704d4adcee8Sschwarze 
1705b7fc66beSschwarze static void
1706b7fc66beSschwarze pre_noarg(DECL_ARGS)
1707b7fc66beSschwarze {
1708b7fc66beSschwarze 	outflags |= MMAN_nl;
1709b7fc66beSschwarze 	print_word(".");
1710b7fc66beSschwarze 	outflags &= ~MMAN_spc;
1711b7fc66beSschwarze 	print_word(roff_name[n->tok]);
1712b7fc66beSschwarze 	outflags |= MMAN_nl;
1713b7fc66beSschwarze }
1714b7fc66beSschwarze 
1715d4adcee8Sschwarze static int
171675d4d0e5Sschwarze pre_ns(DECL_ARGS)
171775d4d0e5Sschwarze {
171851c88ffdSschwarze 	outflags &= ~MMAN_spc;
1719526e306bSschwarze 	return 0;
172075d4d0e5Sschwarze }
172175d4d0e5Sschwarze 
1722e6ab379cSschwarze static void
1723e6ab379cSschwarze post_pf(DECL_ARGS)
1724e6ab379cSschwarze {
1725e6ab379cSschwarze 
1726c4b0939cSschwarze 	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
172751c88ffdSschwarze 		outflags &= ~MMAN_spc;
1728e6ab379cSschwarze }
1729e6ab379cSschwarze 
173075d4d0e5Sschwarze static int
173175d4d0e5Sschwarze pre_pp(DECL_ARGS)
173275d4d0e5Sschwarze {
173375d4d0e5Sschwarze 
173454cad943Sschwarze 	if (MDOC_It != n->parent->tok)
173554cad943Sschwarze 		outflags |= MMAN_PP;
173654cad943Sschwarze 	outflags |= MMAN_sp | MMAN_nl;
173754cad943Sschwarze 	outflags &= ~MMAN_br;
1738526e306bSschwarze 	return 0;
1739e3ceda8aSschwarze }
1740e3ceda8aSschwarze 
1741e3ceda8aSschwarze static int
1742e3ceda8aSschwarze pre_rs(DECL_ARGS)
1743e3ceda8aSschwarze {
1744e3ceda8aSschwarze 
1745e3ceda8aSschwarze 	if (SEC_SEE_ALSO == n->sec) {
174654cad943Sschwarze 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
174754cad943Sschwarze 		outflags &= ~MMAN_br;
1748e3ceda8aSschwarze 	}
1749526e306bSschwarze 	return 1;
175075d4d0e5Sschwarze }
175175d4d0e5Sschwarze 
175275d4d0e5Sschwarze static int
175378bbbab4Sschwarze pre_skip(DECL_ARGS)
175478bbbab4Sschwarze {
175578bbbab4Sschwarze 
1756526e306bSschwarze 	return 0;
175778bbbab4Sschwarze }
175878bbbab4Sschwarze 
175978bbbab4Sschwarze static int
1760c8addc6fSschwarze pre_sm(DECL_ARGS)
1761c8addc6fSschwarze {
1762c8addc6fSschwarze 
1763f9e7bf99Sschwarze 	if (NULL == n->child)
1764f9e7bf99Sschwarze 		outflags ^= MMAN_Sm;
1765f9e7bf99Sschwarze 	else if (0 == strcmp("on", n->child->string))
1766f9e7bf99Sschwarze 		outflags |= MMAN_Sm;
1767c8addc6fSschwarze 	else
176851c88ffdSschwarze 		outflags &= ~MMAN_Sm;
1769f9e7bf99Sschwarze 
1770f9e7bf99Sschwarze 	if (MMAN_Sm & outflags)
1771f9e7bf99Sschwarze 		outflags |= MMAN_spc;
1772f9e7bf99Sschwarze 
1773526e306bSschwarze 	return 0;
1774c8addc6fSschwarze }
1775c8addc6fSschwarze 
17766561cb23Sschwarze static void
1777e6ab379cSschwarze pre_sp(DECL_ARGS)
177875d4d0e5Sschwarze {
17796561cb23Sschwarze 	if (outflags & MMAN_PP) {
1780da5e360cSschwarze 		outflags &= ~MMAN_PP;
1781da5e360cSschwarze 		print_line(".PP", 0);
17826561cb23Sschwarze 	} else {
1783da5e360cSschwarze 		print_line(".sp", 0);
17846561cb23Sschwarze 		if (n->child != NULL)
17856561cb23Sschwarze 			print_word(n->child->string);
178675d4d0e5Sschwarze 	}
178751c88ffdSschwarze 	outflags |= MMAN_nl;
178875d4d0e5Sschwarze }
178975d4d0e5Sschwarze 
179075d4d0e5Sschwarze static int
1791388939aeSschwarze pre_sy(DECL_ARGS)
1792388939aeSschwarze {
1793388939aeSschwarze 
1794388939aeSschwarze 	font_push('B');
1795526e306bSschwarze 	return 1;
1796388939aeSschwarze }
1797388939aeSschwarze 
1798f7242c43Sschwarze static void
1799f7242c43Sschwarze pre_ta(DECL_ARGS)
1800f7242c43Sschwarze {
1801f7242c43Sschwarze 	print_line(".ta", 0);
1802f7242c43Sschwarze 	for (n = n->child; n != NULL; n = n->next)
1803f7242c43Sschwarze 		print_word(n->string);
1804f7242c43Sschwarze 	outflags |= MMAN_nl;
1805f7242c43Sschwarze }
1806f7242c43Sschwarze 
1807388939aeSschwarze static int
1808bce51a71Sschwarze pre_vt(DECL_ARGS)
1809bce51a71Sschwarze {
1810bce51a71Sschwarze 
1811c4b0939cSschwarze 	if (NODE_SYNPRETTY & n->flags) {
1812e4315607Sschwarze 		switch (n->type) {
1813d1982c71Sschwarze 		case ROFFT_BLOCK:
1814fb6b7277Sschwarze 			pre_syn(n);
1815526e306bSschwarze 			return 1;
1816d1982c71Sschwarze 		case ROFFT_BODY:
1817e4315607Sschwarze 			break;
1818e4315607Sschwarze 		default:
1819526e306bSschwarze 			return 0;
1820e4315607Sschwarze 		}
1821bce51a71Sschwarze 	}
1822388939aeSschwarze 	font_push('I');
1823526e306bSschwarze 	return 1;
1824bce51a71Sschwarze }
1825bce51a71Sschwarze 
1826bce51a71Sschwarze static void
1827bce51a71Sschwarze post_vt(DECL_ARGS)
1828bce51a71Sschwarze {
1829bce51a71Sschwarze 
1830c4b0939cSschwarze 	if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
1831e4315607Sschwarze 		return;
1832388939aeSschwarze 	font_pop();
1833bce51a71Sschwarze }
1834bce51a71Sschwarze 
1835bce51a71Sschwarze static int
183675d4d0e5Sschwarze pre_xr(DECL_ARGS)
183775d4d0e5Sschwarze {
183875d4d0e5Sschwarze 
183975d4d0e5Sschwarze 	n = n->child;
184075d4d0e5Sschwarze 	if (NULL == n)
1841526e306bSschwarze 		return 0;
18427ead8a4eSschwarze 	print_node(meta, n);
184375d4d0e5Sschwarze 	n = n->next;
184475d4d0e5Sschwarze 	if (NULL == n)
1845526e306bSschwarze 		return 0;
184651c88ffdSschwarze 	outflags &= ~MMAN_spc;
184751c88ffdSschwarze 	print_word("(");
18487ead8a4eSschwarze 	print_node(meta, n);
184951c88ffdSschwarze 	print_word(")");
1850526e306bSschwarze 	return 0;
185175d4d0e5Sschwarze }
1852