1*4d131170SRobert Mustacchi /* $Id: mdoc_markdown.c,v 1.37 2021/08/10 12:55:03 schwarze Exp $ */
2c66b8046SYuri Pankov /*
3*4d131170SRobert Mustacchi  * Copyright (c) 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org>
4c66b8046SYuri Pankov  *
5c66b8046SYuri Pankov  * Permission to use, copy, modify, and distribute this software for any
6c66b8046SYuri Pankov  * purpose with or without fee is hereby granted, provided that the above
7c66b8046SYuri Pankov  * copyright notice and this permission notice appear in all copies.
8c66b8046SYuri Pankov  *
9c66b8046SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10c66b8046SYuri Pankov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11c66b8046SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12c66b8046SYuri Pankov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13c66b8046SYuri Pankov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14c66b8046SYuri Pankov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15c66b8046SYuri Pankov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*4d131170SRobert Mustacchi  *
17*4d131170SRobert Mustacchi  * Markdown formatter for mdoc(7) used by mandoc(1).
18c66b8046SYuri Pankov  */
19*4d131170SRobert Mustacchi #include "config.h"
20*4d131170SRobert Mustacchi 
21c66b8046SYuri Pankov #include <sys/types.h>
22c66b8046SYuri Pankov 
23c66b8046SYuri Pankov #include <assert.h>
24c66b8046SYuri Pankov #include <ctype.h>
25c66b8046SYuri Pankov #include <stdio.h>
26cec8643bSMichal Nowak #include <stdlib.h>
27c66b8046SYuri Pankov #include <string.h>
28c66b8046SYuri Pankov 
29c66b8046SYuri Pankov #include "mandoc_aux.h"
30c66b8046SYuri Pankov #include "mandoc.h"
31c66b8046SYuri Pankov #include "roff.h"
32c66b8046SYuri Pankov #include "mdoc.h"
33c66b8046SYuri Pankov #include "main.h"
34c66b8046SYuri Pankov 
35c66b8046SYuri Pankov struct	md_act {
36*4d131170SRobert Mustacchi 	int		(*cond)(struct roff_node *);
37*4d131170SRobert Mustacchi 	int		(*pre)(struct roff_node *);
38*4d131170SRobert Mustacchi 	void		(*post)(struct roff_node *);
39c66b8046SYuri Pankov 	const char	 *prefix; /* pre-node string constant */
40c66b8046SYuri Pankov 	const char	 *suffix; /* post-node string constant */
41c66b8046SYuri Pankov };
42c66b8046SYuri Pankov 
43c66b8046SYuri Pankov static	void	 md_nodelist(struct roff_node *);
44c66b8046SYuri Pankov static	void	 md_node(struct roff_node *);
45*4d131170SRobert Mustacchi static	const char *md_stack(char);
46c66b8046SYuri Pankov static	void	 md_preword(void);
47c66b8046SYuri Pankov static	void	 md_rawword(const char *);
48c66b8046SYuri Pankov static	void	 md_word(const char *);
49c66b8046SYuri Pankov static	void	 md_named(const char *);
50c66b8046SYuri Pankov static	void	 md_char(unsigned char);
51c66b8046SYuri Pankov static	void	 md_uri(const char *);
52c66b8046SYuri Pankov 
53c66b8046SYuri Pankov static	int	 md_cond_head(struct roff_node *);
54c66b8046SYuri Pankov static	int	 md_cond_body(struct roff_node *);
55c66b8046SYuri Pankov 
56cec8643bSMichal Nowak static	int	 md_pre_abort(struct roff_node *);
57c66b8046SYuri Pankov static	int	 md_pre_raw(struct roff_node *);
58c66b8046SYuri Pankov static	int	 md_pre_word(struct roff_node *);
59c66b8046SYuri Pankov static	int	 md_pre_skip(struct roff_node *);
60c66b8046SYuri Pankov static	void	 md_pre_syn(struct roff_node *);
61c66b8046SYuri Pankov static	int	 md_pre_An(struct roff_node *);
62c66b8046SYuri Pankov static	int	 md_pre_Ap(struct roff_node *);
63c66b8046SYuri Pankov static	int	 md_pre_Bd(struct roff_node *);
64c66b8046SYuri Pankov static	int	 md_pre_Bk(struct roff_node *);
65c66b8046SYuri Pankov static	int	 md_pre_Bl(struct roff_node *);
66c66b8046SYuri Pankov static	int	 md_pre_D1(struct roff_node *);
67c66b8046SYuri Pankov static	int	 md_pre_Dl(struct roff_node *);
68c66b8046SYuri Pankov static	int	 md_pre_En(struct roff_node *);
69c66b8046SYuri Pankov static	int	 md_pre_Eo(struct roff_node *);
70c66b8046SYuri Pankov static	int	 md_pre_Fa(struct roff_node *);
71c66b8046SYuri Pankov static	int	 md_pre_Fd(struct roff_node *);
72c66b8046SYuri Pankov static	int	 md_pre_Fn(struct roff_node *);
73c66b8046SYuri Pankov static	int	 md_pre_Fo(struct roff_node *);
74c66b8046SYuri Pankov static	int	 md_pre_In(struct roff_node *);
75c66b8046SYuri Pankov static	int	 md_pre_It(struct roff_node *);
76c66b8046SYuri Pankov static	int	 md_pre_Lk(struct roff_node *);
77c66b8046SYuri Pankov static	int	 md_pre_Mt(struct roff_node *);
78c66b8046SYuri Pankov static	int	 md_pre_Nd(struct roff_node *);
79c66b8046SYuri Pankov static	int	 md_pre_Nm(struct roff_node *);
80c66b8046SYuri Pankov static	int	 md_pre_No(struct roff_node *);
81c66b8046SYuri Pankov static	int	 md_pre_Ns(struct roff_node *);
82c66b8046SYuri Pankov static	int	 md_pre_Pp(struct roff_node *);
83c66b8046SYuri Pankov static	int	 md_pre_Rs(struct roff_node *);
84c66b8046SYuri Pankov static	int	 md_pre_Sh(struct roff_node *);
85c66b8046SYuri Pankov static	int	 md_pre_Sm(struct roff_node *);
86c66b8046SYuri Pankov static	int	 md_pre_Vt(struct roff_node *);
87c66b8046SYuri Pankov static	int	 md_pre_Xr(struct roff_node *);
88c66b8046SYuri Pankov static	int	 md_pre__T(struct roff_node *);
89c66b8046SYuri Pankov static	int	 md_pre_br(struct roff_node *);
90c66b8046SYuri Pankov 
91c66b8046SYuri Pankov static	void	 md_post_raw(struct roff_node *);
92c66b8046SYuri Pankov static	void	 md_post_word(struct roff_node *);
93c66b8046SYuri Pankov static	void	 md_post_pc(struct roff_node *);
94c66b8046SYuri Pankov static	void	 md_post_Bk(struct roff_node *);
95c66b8046SYuri Pankov static	void	 md_post_Bl(struct roff_node *);
96c66b8046SYuri Pankov static	void	 md_post_D1(struct roff_node *);
97c66b8046SYuri Pankov static	void	 md_post_En(struct roff_node *);
98c66b8046SYuri Pankov static	void	 md_post_Eo(struct roff_node *);
99c66b8046SYuri Pankov static	void	 md_post_Fa(struct roff_node *);
100c66b8046SYuri Pankov static	void	 md_post_Fd(struct roff_node *);
101c66b8046SYuri Pankov static	void	 md_post_Fl(struct roff_node *);
102c66b8046SYuri Pankov static	void	 md_post_Fn(struct roff_node *);
103c66b8046SYuri Pankov static	void	 md_post_Fo(struct roff_node *);
104c66b8046SYuri Pankov static	void	 md_post_In(struct roff_node *);
105c66b8046SYuri Pankov static	void	 md_post_It(struct roff_node *);
106c66b8046SYuri Pankov static	void	 md_post_Lb(struct roff_node *);
107c66b8046SYuri Pankov static	void	 md_post_Nm(struct roff_node *);
108c66b8046SYuri Pankov static	void	 md_post_Pf(struct roff_node *);
109c66b8046SYuri Pankov static	void	 md_post_Vt(struct roff_node *);
110c66b8046SYuri Pankov static	void	 md_post__T(struct roff_node *);
111c66b8046SYuri Pankov 
112cec8643bSMichal Nowak static	const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = {
113c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
114c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
115c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
116c66b8046SYuri Pankov 	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
117c66b8046SYuri Pankov 	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
118c66b8046SYuri Pankov 	{ NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
119c66b8046SYuri Pankov 	{ md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
120c66b8046SYuri Pankov 	{ md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
121c66b8046SYuri Pankov 	{ md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
122c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
123c66b8046SYuri Pankov 	{ md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
124c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
125c66b8046SYuri Pankov 	{ NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
126c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
127c66b8046SYuri Pankov 	{ NULL, md_pre_An, NULL, NULL, NULL }, /* An */
128c66b8046SYuri Pankov 	{ NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
129c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
130c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
131c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
132c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
133c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
134c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
135c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ex */
136c66b8046SYuri Pankov 	{ NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
137c66b8046SYuri Pankov 	{ NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
138c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
139c66b8046SYuri Pankov 	{ NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
140c66b8046SYuri Pankov 	{ NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
141c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
142c66b8046SYuri Pankov 	{ NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
143c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
144c66b8046SYuri Pankov 	{ md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
145c66b8046SYuri Pankov 	{ NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
146c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
147cec8643bSMichal Nowak 	{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */
148c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
149c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Rv */
150c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
151c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
152c66b8046SYuri Pankov 	{ NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
153c66b8046SYuri Pankov 	{ NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
154c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
155c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
156c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
157c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
158c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
159c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
160c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
161c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
162c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
163c66b8046SYuri Pankov 	{ NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
164c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
165c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
166c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
167c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
168c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
169c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
170c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
171c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
172c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
173c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Bsx */
174c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Bx */
175c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
176c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
177c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
178c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
179c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
180c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
181c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
182c66b8046SYuri Pankov 	{ md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
183c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Fx */
184c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
185c66b8046SYuri Pankov 	{ NULL, md_pre_No, NULL, NULL, NULL }, /* No */
186c66b8046SYuri Pankov 	{ NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
187c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Nx */
188c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ox */
189c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
190c66b8046SYuri Pankov 	{ NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
191c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
192c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
193c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
194c66b8046SYuri Pankov 	{ md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
195c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
196c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
197c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
198c66b8046SYuri Pankov 	{ md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
199c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
200c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
201c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
202c66b8046SYuri Pankov 	{ NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
203c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
204c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
205c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
206c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ux */
207c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
208c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
209c66b8046SYuri Pankov 	{ NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
210c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
211c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
212c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
213c66b8046SYuri Pankov 	{ NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
214c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
215c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Bt */
216c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
217c66b8046SYuri Pankov 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
218c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
219c66b8046SYuri Pankov 	{ NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
220cec8643bSMichal Nowak 	{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */
221c66b8046SYuri Pankov 	{ NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
222c66b8046SYuri Pankov 	{ NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
223c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
224c66b8046SYuri Pankov 	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
225c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
226c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
227c66b8046SYuri Pankov 	{ NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
228c66b8046SYuri Pankov 	{ md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
229c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Dx */
230c66b8046SYuri Pankov 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
231c66b8046SYuri Pankov 	{ NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
232c66b8046SYuri Pankov 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
233*4d131170SRobert Mustacchi 	{ NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */
234c66b8046SYuri Pankov };
235cec8643bSMichal Nowak static const struct md_act *md_act(enum roff_tok);
236c66b8046SYuri Pankov 
237c66b8046SYuri Pankov static	int	 outflags;
238c66b8046SYuri Pankov #define	MD_spc		 (1 << 0)  /* Blank character before next word. */
239c66b8046SYuri Pankov #define	MD_spc_force	 (1 << 1)  /* Even before trailing punctuation. */
240c66b8046SYuri Pankov #define	MD_nonl		 (1 << 2)  /* Prevent linebreak in markdown code. */
241c66b8046SYuri Pankov #define	MD_nl		 (1 << 3)  /* Break markdown code line. */
242c66b8046SYuri Pankov #define	MD_br		 (1 << 4)  /* Insert an output line break. */
243c66b8046SYuri Pankov #define	MD_sp		 (1 << 5)  /* Insert a paragraph break. */
244c66b8046SYuri Pankov #define	MD_Sm		 (1 << 6)  /* Horizontal spacing mode. */
245c66b8046SYuri Pankov #define	MD_Bk		 (1 << 7)  /* Word keep mode. */
246c66b8046SYuri Pankov #define	MD_An_split	 (1 << 8)  /* Author mode is "split". */
247c66b8046SYuri Pankov #define	MD_An_nosplit	 (1 << 9)  /* Author mode is "nosplit". */
248c66b8046SYuri Pankov 
249c66b8046SYuri Pankov static	int	 escflags; /* Escape in generated markdown code: */
250c66b8046SYuri Pankov #define	ESC_BOL	 (1 << 0)  /* "#*+-" near the beginning of a line. */
251c66b8046SYuri Pankov #define	ESC_NUM	 (1 << 1)  /* "." after a leading number. */
252c66b8046SYuri Pankov #define	ESC_HYP	 (1 << 2)  /* "(" immediately after "]". */
253c66b8046SYuri Pankov #define	ESC_SQU	 (1 << 4)  /* "]" when "[" is open. */
254c66b8046SYuri Pankov #define	ESC_FON	 (1 << 5)  /* "*" immediately after unrelated "*". */
255c66b8046SYuri Pankov #define	ESC_EOL	 (1 << 6)  /* " " at the and of a line. */
256c66b8046SYuri Pankov 
257c66b8046SYuri Pankov static	int	 code_blocks, quote_blocks, list_blocks;
258c66b8046SYuri Pankov static	int	 outcount;
259c66b8046SYuri Pankov 
260cec8643bSMichal Nowak 
261cec8643bSMichal Nowak static const struct md_act *
md_act(enum roff_tok tok)262cec8643bSMichal Nowak md_act(enum roff_tok tok)
263cec8643bSMichal Nowak {
264cec8643bSMichal Nowak 	assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
265cec8643bSMichal Nowak 	return md_acts + (tok - MDOC_Dd);
266cec8643bSMichal Nowak }
267cec8643bSMichal Nowak 
268c66b8046SYuri Pankov void
markdown_mdoc(void * arg,const struct roff_meta * mdoc)269cec8643bSMichal Nowak markdown_mdoc(void *arg, const struct roff_meta *mdoc)
270c66b8046SYuri Pankov {
271c66b8046SYuri Pankov 	outflags = MD_Sm;
272cec8643bSMichal Nowak 	md_word(mdoc->title);
273cec8643bSMichal Nowak 	if (mdoc->msec != NULL) {
274c66b8046SYuri Pankov 		outflags &= ~MD_spc;
275c66b8046SYuri Pankov 		md_word("(");
276cec8643bSMichal Nowak 		md_word(mdoc->msec);
277c66b8046SYuri Pankov 		md_word(")");
278c66b8046SYuri Pankov 	}
279c66b8046SYuri Pankov 	md_word("-");
280cec8643bSMichal Nowak 	md_word(mdoc->vol);
281cec8643bSMichal Nowak 	if (mdoc->arch != NULL) {
282c66b8046SYuri Pankov 		md_word("(");
283cec8643bSMichal Nowak 		md_word(mdoc->arch);
284c66b8046SYuri Pankov 		md_word(")");
285c66b8046SYuri Pankov 	}
286c66b8046SYuri Pankov 	outflags |= MD_sp;
287c66b8046SYuri Pankov 
288c66b8046SYuri Pankov 	md_nodelist(mdoc->first->child);
289c66b8046SYuri Pankov 
290c66b8046SYuri Pankov 	outflags |= MD_sp;
291cec8643bSMichal Nowak 	md_word(mdoc->os);
292c66b8046SYuri Pankov 	md_word("-");
293cec8643bSMichal Nowak 	md_word(mdoc->date);
294c66b8046SYuri Pankov 	putchar('\n');
295c66b8046SYuri Pankov }
296c66b8046SYuri Pankov 
297c66b8046SYuri Pankov static void
md_nodelist(struct roff_node * n)298c66b8046SYuri Pankov md_nodelist(struct roff_node *n)
299c66b8046SYuri Pankov {
300c66b8046SYuri Pankov 	while (n != NULL) {
301c66b8046SYuri Pankov 		md_node(n);
302c66b8046SYuri Pankov 		n = n->next;
303c66b8046SYuri Pankov 	}
304c66b8046SYuri Pankov }
305c66b8046SYuri Pankov 
306c66b8046SYuri Pankov static void
md_node(struct roff_node * n)307c66b8046SYuri Pankov md_node(struct roff_node *n)
308c66b8046SYuri Pankov {
309c66b8046SYuri Pankov 	const struct md_act	*act;
310c66b8046SYuri Pankov 	int			 cond, process_children;
311c66b8046SYuri Pankov 
3126640c13bSYuri Pankov 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
313c66b8046SYuri Pankov 		return;
314c66b8046SYuri Pankov 
315c66b8046SYuri Pankov 	if (outflags & MD_nonl)
316c66b8046SYuri Pankov 		outflags &= ~(MD_nl | MD_sp);
317*4d131170SRobert Mustacchi 	else if (outflags & MD_spc &&
318*4d131170SRobert Mustacchi 	     n->flags & NODE_LINE &&
319*4d131170SRobert Mustacchi 	     !roff_node_transparent(n))
320c66b8046SYuri Pankov 		outflags |= MD_nl;
321c66b8046SYuri Pankov 
322c66b8046SYuri Pankov 	act = NULL;
323c66b8046SYuri Pankov 	cond = 0;
324c66b8046SYuri Pankov 	process_children = 1;
325c66b8046SYuri Pankov 	n->flags &= ~NODE_ENDED;
326c66b8046SYuri Pankov 
327c66b8046SYuri Pankov 	if (n->type == ROFFT_TEXT) {
328c66b8046SYuri Pankov 		if (n->flags & NODE_DELIMC)
329c66b8046SYuri Pankov 			outflags &= ~(MD_spc | MD_spc_force);
330c66b8046SYuri Pankov 		else if (outflags & MD_Sm)
331c66b8046SYuri Pankov 			outflags |= MD_spc_force;
332c66b8046SYuri Pankov 		md_word(n->string);
333c66b8046SYuri Pankov 		if (n->flags & NODE_DELIMO)
334c66b8046SYuri Pankov 			outflags &= ~(MD_spc | MD_spc_force);
335c66b8046SYuri Pankov 		else if (outflags & MD_Sm)
336c66b8046SYuri Pankov 			outflags |= MD_spc;
337c66b8046SYuri Pankov 	} else if (n->tok < ROFF_MAX) {
338c66b8046SYuri Pankov 		switch (n->tok) {
339c66b8046SYuri Pankov 		case ROFF_br:
340c66b8046SYuri Pankov 			process_children = md_pre_br(n);
341c66b8046SYuri Pankov 			break;
342c66b8046SYuri Pankov 		case ROFF_sp:
343c66b8046SYuri Pankov 			process_children = md_pre_Pp(n);
344c66b8046SYuri Pankov 			break;
345c66b8046SYuri Pankov 		default:
346c66b8046SYuri Pankov 			process_children = 0;
347c66b8046SYuri Pankov 			break;
348c66b8046SYuri Pankov 		}
349c66b8046SYuri Pankov 	} else {
350cec8643bSMichal Nowak 		act = md_act(n->tok);
351c66b8046SYuri Pankov 		cond = act->cond == NULL || (*act->cond)(n);
352c66b8046SYuri Pankov 		if (cond && act->pre != NULL &&
353c66b8046SYuri Pankov 		    (n->end == ENDBODY_NOT || n->child != NULL))
354c66b8046SYuri Pankov 			process_children = (*act->pre)(n);
355c66b8046SYuri Pankov 	}
356c66b8046SYuri Pankov 
357c66b8046SYuri Pankov 	if (process_children && n->child != NULL)
358c66b8046SYuri Pankov 		md_nodelist(n->child);
359c66b8046SYuri Pankov 
360c66b8046SYuri Pankov 	if (n->flags & NODE_ENDED)
361c66b8046SYuri Pankov 		return;
362c66b8046SYuri Pankov 
363c66b8046SYuri Pankov 	if (cond && act->post != NULL)
364c66b8046SYuri Pankov 		(*act->post)(n);
365c66b8046SYuri Pankov 
366c66b8046SYuri Pankov 	if (n->end != ENDBODY_NOT)
367c66b8046SYuri Pankov 		n->body->flags |= NODE_ENDED;
368c66b8046SYuri Pankov }
369c66b8046SYuri Pankov 
370c66b8046SYuri Pankov static const char *
md_stack(char c)371c66b8046SYuri Pankov md_stack(char c)
372c66b8046SYuri Pankov {
373c66b8046SYuri Pankov 	static char	*stack;
374c66b8046SYuri Pankov 	static size_t	 sz;
375c66b8046SYuri Pankov 	static size_t	 cur;
376c66b8046SYuri Pankov 
377c66b8046SYuri Pankov 	switch (c) {
378c66b8046SYuri Pankov 	case '\0':
379c66b8046SYuri Pankov 		break;
380c66b8046SYuri Pankov 	case (char)-1:
381c66b8046SYuri Pankov 		assert(cur);
382c66b8046SYuri Pankov 		stack[--cur] = '\0';
383c66b8046SYuri Pankov 		break;
384c66b8046SYuri Pankov 	default:
385c66b8046SYuri Pankov 		if (cur + 1 >= sz) {
386c66b8046SYuri Pankov 			sz += 8;
387c66b8046SYuri Pankov 			stack = mandoc_realloc(stack, sz);
388c66b8046SYuri Pankov 		}
389c66b8046SYuri Pankov 		stack[cur] = c;
390c66b8046SYuri Pankov 		stack[++cur] = '\0';
391c66b8046SYuri Pankov 		break;
392c66b8046SYuri Pankov 	}
393c66b8046SYuri Pankov 	return stack == NULL ? "" : stack;
394c66b8046SYuri Pankov }
395c66b8046SYuri Pankov 
396c66b8046SYuri Pankov /*
397c66b8046SYuri Pankov  * Handle vertical and horizontal spacing.
398c66b8046SYuri Pankov  */
399c66b8046SYuri Pankov static void
md_preword(void)400c66b8046SYuri Pankov md_preword(void)
401c66b8046SYuri Pankov {
402c66b8046SYuri Pankov 	const char	*cp;
403c66b8046SYuri Pankov 
404c66b8046SYuri Pankov 	/*
405c66b8046SYuri Pankov 	 * If a list block is nested inside a code block or a blockquote,
406c66b8046SYuri Pankov 	 * blank lines for paragraph breaks no longer work; instead,
407c66b8046SYuri Pankov 	 * they terminate the list.  Work around this markdown issue
408c66b8046SYuri Pankov 	 * by using mere line breaks instead.
409c66b8046SYuri Pankov 	 */
410c66b8046SYuri Pankov 
411c66b8046SYuri Pankov 	if (list_blocks && outflags & MD_sp) {
412c66b8046SYuri Pankov 		outflags &= ~MD_sp;
413c66b8046SYuri Pankov 		outflags |= MD_br;
414c66b8046SYuri Pankov 	}
415c66b8046SYuri Pankov 
416c66b8046SYuri Pankov 	/*
417c66b8046SYuri Pankov 	 * End the old line if requested.
418c66b8046SYuri Pankov 	 * Escape whitespace at the end of the markdown line
419c66b8046SYuri Pankov 	 * such that it won't look like an output line break.
420c66b8046SYuri Pankov 	 */
421c66b8046SYuri Pankov 
422c66b8046SYuri Pankov 	if (outflags & MD_sp)
423c66b8046SYuri Pankov 		putchar('\n');
424c66b8046SYuri Pankov 	else if (outflags & MD_br) {
425c66b8046SYuri Pankov 		putchar(' ');
426c66b8046SYuri Pankov 		putchar(' ');
427c66b8046SYuri Pankov 	} else if (outflags & MD_nl && escflags & ESC_EOL)
428c66b8046SYuri Pankov 		md_named("zwnj");
429c66b8046SYuri Pankov 
430c66b8046SYuri Pankov 	/* Start a new line if necessary. */
431c66b8046SYuri Pankov 
432c66b8046SYuri Pankov 	if (outflags & (MD_nl | MD_br | MD_sp)) {
433c66b8046SYuri Pankov 		putchar('\n');
434c66b8046SYuri Pankov 		for (cp = md_stack('\0'); *cp != '\0'; cp++) {
435c66b8046SYuri Pankov 			putchar(*cp);
436c66b8046SYuri Pankov 			if (*cp == '>')
437c66b8046SYuri Pankov 				putchar(' ');
438c66b8046SYuri Pankov 		}
439c66b8046SYuri Pankov 		outflags &= ~(MD_nl | MD_br | MD_sp);
440c66b8046SYuri Pankov 		escflags = ESC_BOL;
441c66b8046SYuri Pankov 		outcount = 0;
442c66b8046SYuri Pankov 
443c66b8046SYuri Pankov 	/* Handle horizontal spacing. */
444c66b8046SYuri Pankov 
445c66b8046SYuri Pankov 	} else if (outflags & MD_spc) {
446c66b8046SYuri Pankov 		if (outflags & MD_Bk)
447c66b8046SYuri Pankov 			fputs("&nbsp;", stdout);
448c66b8046SYuri Pankov 		else
449c66b8046SYuri Pankov 			putchar(' ');
450c66b8046SYuri Pankov 		escflags &= ~ESC_FON;
451c66b8046SYuri Pankov 		outcount++;
452c66b8046SYuri Pankov 	}
453c66b8046SYuri Pankov 
454c66b8046SYuri Pankov 	outflags &= ~(MD_spc_force | MD_nonl);
455c66b8046SYuri Pankov 	if (outflags & MD_Sm)
456c66b8046SYuri Pankov 		outflags |= MD_spc;
457c66b8046SYuri Pankov 	else
458c66b8046SYuri Pankov 		outflags &= ~MD_spc;
459c66b8046SYuri Pankov }
460c66b8046SYuri Pankov 
461c66b8046SYuri Pankov /*
462c66b8046SYuri Pankov  * Print markdown syntax elements.
463c66b8046SYuri Pankov  * Can also be used for constant strings when neither escaping
464c66b8046SYuri Pankov  * nor delimiter handling is required.
465c66b8046SYuri Pankov  */
466c66b8046SYuri Pankov static void
md_rawword(const char * s)467c66b8046SYuri Pankov md_rawword(const char *s)
468c66b8046SYuri Pankov {
469c66b8046SYuri Pankov 	md_preword();
470c66b8046SYuri Pankov 
471c66b8046SYuri Pankov 	if (*s == '\0')
472c66b8046SYuri Pankov 		return;
473c66b8046SYuri Pankov 
474c66b8046SYuri Pankov 	if (escflags & ESC_FON) {
475c66b8046SYuri Pankov 		escflags &= ~ESC_FON;
476c66b8046SYuri Pankov 		if (*s == '*' && !code_blocks)
477c66b8046SYuri Pankov 			fputs("&zwnj;", stdout);
478c66b8046SYuri Pankov 	}
479c66b8046SYuri Pankov 
480c66b8046SYuri Pankov 	while (*s != '\0') {
481c66b8046SYuri Pankov 		switch(*s) {
482c66b8046SYuri Pankov 		case '*':
483c66b8046SYuri Pankov 			if (s[1] == '\0')
484c66b8046SYuri Pankov 				escflags |= ESC_FON;
485c66b8046SYuri Pankov 			break;
486c66b8046SYuri Pankov 		case '[':
487c66b8046SYuri Pankov 			escflags |= ESC_SQU;
488c66b8046SYuri Pankov 			break;
489c66b8046SYuri Pankov 		case ']':
490c66b8046SYuri Pankov 			escflags |= ESC_HYP;
491c66b8046SYuri Pankov 			escflags &= ~ESC_SQU;
492c66b8046SYuri Pankov 			break;
493c66b8046SYuri Pankov 		default:
494c66b8046SYuri Pankov 			break;
495c66b8046SYuri Pankov 		}
496c66b8046SYuri Pankov 		md_char(*s++);
497c66b8046SYuri Pankov 	}
498c66b8046SYuri Pankov 	if (s[-1] == ' ')
499c66b8046SYuri Pankov 		escflags |= ESC_EOL;
500c66b8046SYuri Pankov 	else
501c66b8046SYuri Pankov 		escflags &= ~ESC_EOL;
502c66b8046SYuri Pankov }
503c66b8046SYuri Pankov 
504c66b8046SYuri Pankov /*
505c66b8046SYuri Pankov  * Print text and mdoc(7) syntax elements.
506c66b8046SYuri Pankov  */
507c66b8046SYuri Pankov static void
md_word(const char * s)508c66b8046SYuri Pankov md_word(const char *s)
509c66b8046SYuri Pankov {
510c66b8046SYuri Pankov 	const char	*seq, *prevfont, *currfont, *nextfont;
511c66b8046SYuri Pankov 	char		 c;
512c66b8046SYuri Pankov 	int		 bs, sz, uc, breakline;
513c66b8046SYuri Pankov 
514c66b8046SYuri Pankov 	/* No spacing before closing delimiters. */
515c66b8046SYuri Pankov 	if (s[0] != '\0' && s[1] == '\0' &&
516c66b8046SYuri Pankov 	    strchr("!),.:;?]", s[0]) != NULL &&
517c66b8046SYuri Pankov 	    (outflags & MD_spc_force) == 0)
518c66b8046SYuri Pankov 		outflags &= ~MD_spc;
519c66b8046SYuri Pankov 
520c66b8046SYuri Pankov 	md_preword();
521c66b8046SYuri Pankov 
522c66b8046SYuri Pankov 	if (*s == '\0')
523c66b8046SYuri Pankov 		return;
524c66b8046SYuri Pankov 
525c66b8046SYuri Pankov 	/* No spacing after opening delimiters. */
526c66b8046SYuri Pankov 	if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
527c66b8046SYuri Pankov 		outflags &= ~MD_spc;
528c66b8046SYuri Pankov 
529c66b8046SYuri Pankov 	breakline = 0;
530c66b8046SYuri Pankov 	prevfont = currfont = "";
531c66b8046SYuri Pankov 	while ((c = *s++) != '\0') {
532c66b8046SYuri Pankov 		bs = 0;
533c66b8046SYuri Pankov 		switch(c) {
534c66b8046SYuri Pankov 		case ASCII_NBRSP:
535c66b8046SYuri Pankov 			if (code_blocks)
536c66b8046SYuri Pankov 				c = ' ';
537c66b8046SYuri Pankov 			else {
538c66b8046SYuri Pankov 				md_named("nbsp");
539c66b8046SYuri Pankov 				c = '\0';
540c66b8046SYuri Pankov 			}
541c66b8046SYuri Pankov 			break;
542c66b8046SYuri Pankov 		case ASCII_HYPH:
543c66b8046SYuri Pankov 			bs = escflags & ESC_BOL && !code_blocks;
544c66b8046SYuri Pankov 			c = '-';
545c66b8046SYuri Pankov 			break;
546c66b8046SYuri Pankov 		case ASCII_BREAK:
547c66b8046SYuri Pankov 			continue;
548c66b8046SYuri Pankov 		case '#':
549c66b8046SYuri Pankov 		case '+':
550c66b8046SYuri Pankov 		case '-':
551c66b8046SYuri Pankov 			bs = escflags & ESC_BOL && !code_blocks;
552c66b8046SYuri Pankov 			break;
553c66b8046SYuri Pankov 		case '(':
554c66b8046SYuri Pankov 			bs = escflags & ESC_HYP && !code_blocks;
555c66b8046SYuri Pankov 			break;
556c66b8046SYuri Pankov 		case ')':
557c66b8046SYuri Pankov 			bs = escflags & ESC_NUM && !code_blocks;
558c66b8046SYuri Pankov 			break;
559c66b8046SYuri Pankov 		case '*':
560c66b8046SYuri Pankov 		case '[':
561c66b8046SYuri Pankov 		case '_':
562c66b8046SYuri Pankov 		case '`':
563c66b8046SYuri Pankov 			bs = !code_blocks;
564c66b8046SYuri Pankov 			break;
565c66b8046SYuri Pankov 		case '.':
566c66b8046SYuri Pankov 			bs = escflags & ESC_NUM && !code_blocks;
567c66b8046SYuri Pankov 			break;
568c66b8046SYuri Pankov 		case '<':
569c66b8046SYuri Pankov 			if (code_blocks == 0) {
570c66b8046SYuri Pankov 				md_named("lt");
571c66b8046SYuri Pankov 				c = '\0';
572c66b8046SYuri Pankov 			}
573c66b8046SYuri Pankov 			break;
574c66b8046SYuri Pankov 		case '=':
575c66b8046SYuri Pankov 			if (escflags & ESC_BOL && !code_blocks) {
576c66b8046SYuri Pankov 				md_named("equals");
577c66b8046SYuri Pankov 				c = '\0';
578c66b8046SYuri Pankov 			}
579c66b8046SYuri Pankov 			break;
580c66b8046SYuri Pankov 		case '>':
581c66b8046SYuri Pankov 			if (code_blocks == 0) {
582c66b8046SYuri Pankov 				md_named("gt");
583c66b8046SYuri Pankov 				c = '\0';
584c66b8046SYuri Pankov 			}
585c66b8046SYuri Pankov 			break;
586c66b8046SYuri Pankov 		case '\\':
587c66b8046SYuri Pankov 			uc = 0;
588c66b8046SYuri Pankov 			nextfont = NULL;
589c66b8046SYuri Pankov 			switch (mandoc_escape(&s, &seq, &sz)) {
590c66b8046SYuri Pankov 			case ESCAPE_UNICODE:
591c66b8046SYuri Pankov 				uc = mchars_num2uc(seq + 1, sz - 1);
592c66b8046SYuri Pankov 				break;
593c66b8046SYuri Pankov 			case ESCAPE_NUMBERED:
594c66b8046SYuri Pankov 				uc = mchars_num2char(seq, sz);
595c66b8046SYuri Pankov 				break;
596c66b8046SYuri Pankov 			case ESCAPE_SPECIAL:
597c66b8046SYuri Pankov 				uc = mchars_spec2cp(seq, sz);
598c66b8046SYuri Pankov 				break;
599cec8643bSMichal Nowak 			case ESCAPE_UNDEF:
600cec8643bSMichal Nowak 				uc = *seq;
601cec8643bSMichal Nowak 				break;
602cec8643bSMichal Nowak 			case ESCAPE_DEVICE:
603cec8643bSMichal Nowak 				md_rawword("markdown");
604cec8643bSMichal Nowak 				continue;
605c66b8046SYuri Pankov 			case ESCAPE_FONTBOLD:
606*4d131170SRobert Mustacchi 			case ESCAPE_FONTCB:
607c66b8046SYuri Pankov 				nextfont = "**";
608c66b8046SYuri Pankov 				break;
609c66b8046SYuri Pankov 			case ESCAPE_FONTITALIC:
610*4d131170SRobert Mustacchi 			case ESCAPE_FONTCI:
611c66b8046SYuri Pankov 				nextfont = "*";
612c66b8046SYuri Pankov 				break;
613c66b8046SYuri Pankov 			case ESCAPE_FONTBI:
614c66b8046SYuri Pankov 				nextfont = "***";
615c66b8046SYuri Pankov 				break;
616c66b8046SYuri Pankov 			case ESCAPE_FONT:
617*4d131170SRobert Mustacchi 			case ESCAPE_FONTCR:
618c66b8046SYuri Pankov 			case ESCAPE_FONTROMAN:
619c66b8046SYuri Pankov 				nextfont = "";
620c66b8046SYuri Pankov 				break;
621c66b8046SYuri Pankov 			case ESCAPE_FONTPREV:
622c66b8046SYuri Pankov 				nextfont = prevfont;
623c66b8046SYuri Pankov 				break;
624c66b8046SYuri Pankov 			case ESCAPE_BREAK:
625c66b8046SYuri Pankov 				breakline = 1;
626c66b8046SYuri Pankov 				break;
627c66b8046SYuri Pankov 			case ESCAPE_NOSPACE:
628c66b8046SYuri Pankov 			case ESCAPE_SKIPCHAR:
629c66b8046SYuri Pankov 			case ESCAPE_OVERSTRIKE:
630c66b8046SYuri Pankov 				/* XXX not implemented */
631c66b8046SYuri Pankov 				/* FALLTHROUGH */
632c66b8046SYuri Pankov 			case ESCAPE_ERROR:
633c66b8046SYuri Pankov 			default:
634c66b8046SYuri Pankov 				break;
635c66b8046SYuri Pankov 			}
636c66b8046SYuri Pankov 			if (nextfont != NULL && !code_blocks) {
637c66b8046SYuri Pankov 				if (*currfont != '\0') {
638c66b8046SYuri Pankov 					outflags &= ~MD_spc;
639c66b8046SYuri Pankov 					md_rawword(currfont);
640c66b8046SYuri Pankov 				}
641c66b8046SYuri Pankov 				prevfont = currfont;
642c66b8046SYuri Pankov 				currfont = nextfont;
643c66b8046SYuri Pankov 				if (*currfont != '\0') {
644c66b8046SYuri Pankov 					outflags &= ~MD_spc;
645c66b8046SYuri Pankov 					md_rawword(currfont);
646c66b8046SYuri Pankov 				}
647c66b8046SYuri Pankov 			}
648c66b8046SYuri Pankov 			if (uc) {
649c66b8046SYuri Pankov 				if ((uc < 0x20 && uc != 0x09) ||
650c66b8046SYuri Pankov 				    (uc > 0x7E && uc < 0xA0))
651c66b8046SYuri Pankov 					uc = 0xFFFD;
652c66b8046SYuri Pankov 				if (code_blocks) {
653c66b8046SYuri Pankov 					seq = mchars_uc2str(uc);
654c66b8046SYuri Pankov 					fputs(seq, stdout);
655c66b8046SYuri Pankov 					outcount += strlen(seq);
656c66b8046SYuri Pankov 				} else {
657c66b8046SYuri Pankov 					printf("&#%d;", uc);
658c66b8046SYuri Pankov 					outcount++;
659c66b8046SYuri Pankov 				}
660c66b8046SYuri Pankov 				escflags &= ~ESC_FON;
661c66b8046SYuri Pankov 			}
662c66b8046SYuri Pankov 			c = '\0';
663c66b8046SYuri Pankov 			break;
664c66b8046SYuri Pankov 		case ']':
665c66b8046SYuri Pankov 			bs = escflags & ESC_SQU && !code_blocks;
666c66b8046SYuri Pankov 			escflags |= ESC_HYP;
667c66b8046SYuri Pankov 			break;
668c66b8046SYuri Pankov 		default:
669c66b8046SYuri Pankov 			break;
670c66b8046SYuri Pankov 		}
671c66b8046SYuri Pankov 		if (bs)
672c66b8046SYuri Pankov 			putchar('\\');
673c66b8046SYuri Pankov 		md_char(c);
674c66b8046SYuri Pankov 		if (breakline &&
675c66b8046SYuri Pankov 		    (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
676c66b8046SYuri Pankov 			printf("  \n");
677c66b8046SYuri Pankov 			breakline = 0;
678c66b8046SYuri Pankov 			while (*s == ' ' || *s == ASCII_NBRSP)
679c66b8046SYuri Pankov 				s++;
680c66b8046SYuri Pankov 		}
681c66b8046SYuri Pankov 	}
682c66b8046SYuri Pankov 	if (*currfont != '\0') {
683c66b8046SYuri Pankov 		outflags &= ~MD_spc;
684c66b8046SYuri Pankov 		md_rawword(currfont);
685c66b8046SYuri Pankov 	} else if (s[-2] == ' ')
686c66b8046SYuri Pankov 		escflags |= ESC_EOL;
687c66b8046SYuri Pankov 	else
688c66b8046SYuri Pankov 		escflags &= ~ESC_EOL;
689c66b8046SYuri Pankov }
690c66b8046SYuri Pankov 
691c66b8046SYuri Pankov /*
692c66b8046SYuri Pankov  * Print a single HTML named character reference.
693c66b8046SYuri Pankov  */
694c66b8046SYuri Pankov static void
md_named(const char * s)695c66b8046SYuri Pankov md_named(const char *s)
696c66b8046SYuri Pankov {
697c66b8046SYuri Pankov 	printf("&%s;", s);
698c66b8046SYuri Pankov 	escflags &= ~(ESC_FON | ESC_EOL);
699c66b8046SYuri Pankov 	outcount++;
700c66b8046SYuri Pankov }
701c66b8046SYuri Pankov 
702c66b8046SYuri Pankov /*
703c66b8046SYuri Pankov  * Print a single raw character and maintain certain escape flags.
704c66b8046SYuri Pankov  */
705c66b8046SYuri Pankov static void
md_char(unsigned char c)706c66b8046SYuri Pankov md_char(unsigned char c)
707c66b8046SYuri Pankov {
708c66b8046SYuri Pankov 	if (c != '\0') {
709c66b8046SYuri Pankov 		putchar(c);
710c66b8046SYuri Pankov 		if (c == '*')
711c66b8046SYuri Pankov 			escflags |= ESC_FON;
712c66b8046SYuri Pankov 		else
713c66b8046SYuri Pankov 			escflags &= ~ESC_FON;
714c66b8046SYuri Pankov 		outcount++;
715c66b8046SYuri Pankov 	}
716c66b8046SYuri Pankov 	if (c != ']')
717c66b8046SYuri Pankov 		escflags &= ~ESC_HYP;
718c66b8046SYuri Pankov 	if (c == ' ' || c == '\t' || c == '>')
719c66b8046SYuri Pankov 		return;
720c66b8046SYuri Pankov 	if (isdigit(c) == 0)
721c66b8046SYuri Pankov 		escflags &= ~ESC_NUM;
722c66b8046SYuri Pankov 	else if (escflags & ESC_BOL)
723c66b8046SYuri Pankov 		escflags |= ESC_NUM;
724c66b8046SYuri Pankov 	escflags &= ~ESC_BOL;
725c66b8046SYuri Pankov }
726c66b8046SYuri Pankov 
727c66b8046SYuri Pankov static int
md_cond_head(struct roff_node * n)728c66b8046SYuri Pankov md_cond_head(struct roff_node *n)
729c66b8046SYuri Pankov {
730c66b8046SYuri Pankov 	return n->type == ROFFT_HEAD;
731c66b8046SYuri Pankov }
732c66b8046SYuri Pankov 
733c66b8046SYuri Pankov static int
md_cond_body(struct roff_node * n)734c66b8046SYuri Pankov md_cond_body(struct roff_node *n)
735c66b8046SYuri Pankov {
736c66b8046SYuri Pankov 	return n->type == ROFFT_BODY;
737c66b8046SYuri Pankov }
738c66b8046SYuri Pankov 
739cec8643bSMichal Nowak static int
md_pre_abort(struct roff_node * n)740cec8643bSMichal Nowak md_pre_abort(struct roff_node *n)
741cec8643bSMichal Nowak {
742cec8643bSMichal Nowak 	abort();
743cec8643bSMichal Nowak }
744cec8643bSMichal Nowak 
745c66b8046SYuri Pankov static int
md_pre_raw(struct roff_node * n)746c66b8046SYuri Pankov md_pre_raw(struct roff_node *n)
747c66b8046SYuri Pankov {
748c66b8046SYuri Pankov 	const char	*prefix;
749c66b8046SYuri Pankov 
750cec8643bSMichal Nowak 	if ((prefix = md_act(n->tok)->prefix) != NULL) {
751c66b8046SYuri Pankov 		md_rawword(prefix);
752c66b8046SYuri Pankov 		outflags &= ~MD_spc;
753c66b8046SYuri Pankov 		if (*prefix == '`')
754c66b8046SYuri Pankov 			code_blocks++;
755c66b8046SYuri Pankov 	}
756c66b8046SYuri Pankov 	return 1;
757c66b8046SYuri Pankov }
758c66b8046SYuri Pankov 
759c66b8046SYuri Pankov static void
md_post_raw(struct roff_node * n)760c66b8046SYuri Pankov md_post_raw(struct roff_node *n)
761c66b8046SYuri Pankov {
762c66b8046SYuri Pankov 	const char	*suffix;
763c66b8046SYuri Pankov 
764cec8643bSMichal Nowak 	if ((suffix = md_act(n->tok)->suffix) != NULL) {
765c66b8046SYuri Pankov 		outflags &= ~(MD_spc | MD_nl);
766c66b8046SYuri Pankov 		md_rawword(suffix);
767c66b8046SYuri Pankov 		if (*suffix == '`')
768c66b8046SYuri Pankov 			code_blocks--;
769c66b8046SYuri Pankov 	}
770c66b8046SYuri Pankov }
771c66b8046SYuri Pankov 
772c66b8046SYuri Pankov static int
md_pre_word(struct roff_node * n)773c66b8046SYuri Pankov md_pre_word(struct roff_node *n)
774c66b8046SYuri Pankov {
775c66b8046SYuri Pankov 	const char	*prefix;
776c66b8046SYuri Pankov 
777cec8643bSMichal Nowak 	if ((prefix = md_act(n->tok)->prefix) != NULL) {
778c66b8046SYuri Pankov 		md_word(prefix);
779c66b8046SYuri Pankov 		outflags &= ~MD_spc;
780c66b8046SYuri Pankov 	}
781c66b8046SYuri Pankov 	return 1;
782c66b8046SYuri Pankov }
783c66b8046SYuri Pankov 
784c66b8046SYuri Pankov static void
md_post_word(struct roff_node * n)785c66b8046SYuri Pankov md_post_word(struct roff_node *n)
786c66b8046SYuri Pankov {
787c66b8046SYuri Pankov 	const char	*suffix;
788c66b8046SYuri Pankov 
789cec8643bSMichal Nowak 	if ((suffix = md_act(n->tok)->suffix) != NULL) {
790c66b8046SYuri Pankov 		outflags &= ~(MD_spc | MD_nl);
791c66b8046SYuri Pankov 		md_word(suffix);
792c66b8046SYuri Pankov 	}
793c66b8046SYuri Pankov }
794c66b8046SYuri Pankov 
795c66b8046SYuri Pankov static void
md_post_pc(struct roff_node * n)796c66b8046SYuri Pankov md_post_pc(struct roff_node *n)
797c66b8046SYuri Pankov {
798*4d131170SRobert Mustacchi 	struct roff_node *nn;
799*4d131170SRobert Mustacchi 
800c66b8046SYuri Pankov 	md_post_raw(n);
801c66b8046SYuri Pankov 	if (n->parent->tok != MDOC_Rs)
802c66b8046SYuri Pankov 		return;
803*4d131170SRobert Mustacchi 
804*4d131170SRobert Mustacchi 	if ((nn = roff_node_next(n)) != NULL) {
805c66b8046SYuri Pankov 		md_word(",");
806*4d131170SRobert Mustacchi 		if (nn->tok == n->tok &&
807*4d131170SRobert Mustacchi 		    (nn = roff_node_prev(n)) != NULL &&
808*4d131170SRobert Mustacchi 		    nn->tok == n->tok)
809c66b8046SYuri Pankov 			md_word("and");
810c66b8046SYuri Pankov 	} else {
811c66b8046SYuri Pankov 		md_word(".");
812c66b8046SYuri Pankov 		outflags |= MD_nl;
813c66b8046SYuri Pankov 	}
814c66b8046SYuri Pankov }
815c66b8046SYuri Pankov 
816c66b8046SYuri Pankov static int
md_pre_skip(struct roff_node * n)817c66b8046SYuri Pankov md_pre_skip(struct roff_node *n)
818c66b8046SYuri Pankov {
819c66b8046SYuri Pankov 	return 0;
820c66b8046SYuri Pankov }
821c66b8046SYuri Pankov 
822c66b8046SYuri Pankov static void
md_pre_syn(struct roff_node * n)823c66b8046SYuri Pankov md_pre_syn(struct roff_node *n)
824c66b8046SYuri Pankov {
825*4d131170SRobert Mustacchi 	struct roff_node *np;
826*4d131170SRobert Mustacchi 
827*4d131170SRobert Mustacchi 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
828*4d131170SRobert Mustacchi 	    (np = roff_node_prev(n)) == NULL)
829c66b8046SYuri Pankov 		return;
830c66b8046SYuri Pankov 
831*4d131170SRobert Mustacchi 	if (np->tok == n->tok &&
832c66b8046SYuri Pankov 	    n->tok != MDOC_Ft &&
833c66b8046SYuri Pankov 	    n->tok != MDOC_Fo &&
834c66b8046SYuri Pankov 	    n->tok != MDOC_Fn) {
835c66b8046SYuri Pankov 		outflags |= MD_br;
836c66b8046SYuri Pankov 		return;
837c66b8046SYuri Pankov 	}
838c66b8046SYuri Pankov 
839*4d131170SRobert Mustacchi 	switch (np->tok) {
840c66b8046SYuri Pankov 	case MDOC_Fd:
841c66b8046SYuri Pankov 	case MDOC_Fn:
842c66b8046SYuri Pankov 	case MDOC_Fo:
843c66b8046SYuri Pankov 	case MDOC_In:
844c66b8046SYuri Pankov 	case MDOC_Vt:
845c66b8046SYuri Pankov 		outflags |= MD_sp;
846c66b8046SYuri Pankov 		break;
847c66b8046SYuri Pankov 	case MDOC_Ft:
848c66b8046SYuri Pankov 		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
849c66b8046SYuri Pankov 			outflags |= MD_sp;
850c66b8046SYuri Pankov 			break;
851c66b8046SYuri Pankov 		}
852c66b8046SYuri Pankov 		/* FALLTHROUGH */
853c66b8046SYuri Pankov 	default:
854c66b8046SYuri Pankov 		outflags |= MD_br;
855c66b8046SYuri Pankov 		break;
856c66b8046SYuri Pankov 	}
857c66b8046SYuri Pankov }
858c66b8046SYuri Pankov 
859c66b8046SYuri Pankov static int
md_pre_An(struct roff_node * n)860c66b8046SYuri Pankov md_pre_An(struct roff_node *n)
861c66b8046SYuri Pankov {
862c66b8046SYuri Pankov 	switch (n->norm->An.auth) {
863c66b8046SYuri Pankov 	case AUTH_split:
864c66b8046SYuri Pankov 		outflags &= ~MD_An_nosplit;
865c66b8046SYuri Pankov 		outflags |= MD_An_split;
866c66b8046SYuri Pankov 		return 0;
867c66b8046SYuri Pankov 	case AUTH_nosplit:
868c66b8046SYuri Pankov 		outflags &= ~MD_An_split;
869c66b8046SYuri Pankov 		outflags |= MD_An_nosplit;
870c66b8046SYuri Pankov 		return 0;
871c66b8046SYuri Pankov 	default:
872c66b8046SYuri Pankov 		if (outflags & MD_An_split)
873c66b8046SYuri Pankov 			outflags |= MD_br;
874c66b8046SYuri Pankov 		else if (n->sec == SEC_AUTHORS &&
875c66b8046SYuri Pankov 		    ! (outflags & MD_An_nosplit))
876c66b8046SYuri Pankov 			outflags |= MD_An_split;
877c66b8046SYuri Pankov 		return 1;
878c66b8046SYuri Pankov 	}
879c66b8046SYuri Pankov }
880c66b8046SYuri Pankov 
881c66b8046SYuri Pankov static int
md_pre_Ap(struct roff_node * n)882c66b8046SYuri Pankov md_pre_Ap(struct roff_node *n)
883c66b8046SYuri Pankov {
884c66b8046SYuri Pankov 	outflags &= ~MD_spc;
885c66b8046SYuri Pankov 	md_word("'");
886c66b8046SYuri Pankov 	outflags &= ~MD_spc;
887c66b8046SYuri Pankov 	return 0;
888c66b8046SYuri Pankov }
889c66b8046SYuri Pankov 
890c66b8046SYuri Pankov static int
md_pre_Bd(struct roff_node * n)891c66b8046SYuri Pankov md_pre_Bd(struct roff_node *n)
892c66b8046SYuri Pankov {
893c66b8046SYuri Pankov 	switch (n->norm->Bd.type) {
894c66b8046SYuri Pankov 	case DISP_unfilled:
895c66b8046SYuri Pankov 	case DISP_literal:
896c66b8046SYuri Pankov 		return md_pre_Dl(n);
897c66b8046SYuri Pankov 	default:
898c66b8046SYuri Pankov 		return md_pre_D1(n);
899c66b8046SYuri Pankov 	}
900c66b8046SYuri Pankov }
901c66b8046SYuri Pankov 
902c66b8046SYuri Pankov static int
md_pre_Bk(struct roff_node * n)903c66b8046SYuri Pankov md_pre_Bk(struct roff_node *n)
904c66b8046SYuri Pankov {
905c66b8046SYuri Pankov 	switch (n->type) {
906c66b8046SYuri Pankov 	case ROFFT_BLOCK:
907c66b8046SYuri Pankov 		return 1;
908c66b8046SYuri Pankov 	case ROFFT_BODY:
909c66b8046SYuri Pankov 		outflags |= MD_Bk;
910c66b8046SYuri Pankov 		return 1;
911c66b8046SYuri Pankov 	default:
912c66b8046SYuri Pankov 		return 0;
913c66b8046SYuri Pankov 	}
914c66b8046SYuri Pankov }
915c66b8046SYuri Pankov 
916c66b8046SYuri Pankov static void
md_post_Bk(struct roff_node * n)917c66b8046SYuri Pankov md_post_Bk(struct roff_node *n)
918c66b8046SYuri Pankov {
919c66b8046SYuri Pankov 	if (n->type == ROFFT_BODY)
920c66b8046SYuri Pankov 		outflags &= ~MD_Bk;
921c66b8046SYuri Pankov }
922c66b8046SYuri Pankov 
923c66b8046SYuri Pankov static int
md_pre_Bl(struct roff_node * n)924c66b8046SYuri Pankov md_pre_Bl(struct roff_node *n)
925c66b8046SYuri Pankov {
926c66b8046SYuri Pankov 	n->norm->Bl.count = 0;
927c66b8046SYuri Pankov 	if (n->norm->Bl.type == LIST_column)
928c66b8046SYuri Pankov 		md_pre_Dl(n);
929c66b8046SYuri Pankov 	outflags |= MD_sp;
930c66b8046SYuri Pankov 	return 1;
931c66b8046SYuri Pankov }
932c66b8046SYuri Pankov 
933c66b8046SYuri Pankov static void
md_post_Bl(struct roff_node * n)934c66b8046SYuri Pankov md_post_Bl(struct roff_node *n)
935c66b8046SYuri Pankov {
936c66b8046SYuri Pankov 	n->norm->Bl.count = 0;
937c66b8046SYuri Pankov 	if (n->norm->Bl.type == LIST_column)
938c66b8046SYuri Pankov 		md_post_D1(n);
939c66b8046SYuri Pankov 	outflags |= MD_sp;
940c66b8046SYuri Pankov }
941c66b8046SYuri Pankov 
942c66b8046SYuri Pankov static int
md_pre_D1(struct roff_node * n)943c66b8046SYuri Pankov md_pre_D1(struct roff_node *n)
944c66b8046SYuri Pankov {
945c66b8046SYuri Pankov 	/*
946c66b8046SYuri Pankov 	 * Markdown blockquote syntax does not work inside code blocks.
947c66b8046SYuri Pankov 	 * The best we can do is fall back to another nested code block.
948c66b8046SYuri Pankov 	 */
949c66b8046SYuri Pankov 	if (code_blocks) {
950c66b8046SYuri Pankov 		md_stack('\t');
951c66b8046SYuri Pankov 		code_blocks++;
952c66b8046SYuri Pankov 	} else {
953c66b8046SYuri Pankov 		md_stack('>');
954c66b8046SYuri Pankov 		quote_blocks++;
955c66b8046SYuri Pankov 	}
956c66b8046SYuri Pankov 	outflags |= MD_sp;
957c66b8046SYuri Pankov 	return 1;
958c66b8046SYuri Pankov }
959c66b8046SYuri Pankov 
960c66b8046SYuri Pankov static void
md_post_D1(struct roff_node * n)961c66b8046SYuri Pankov md_post_D1(struct roff_node *n)
962c66b8046SYuri Pankov {
963c66b8046SYuri Pankov 	md_stack((char)-1);
964c66b8046SYuri Pankov 	if (code_blocks)
965c66b8046SYuri Pankov 		code_blocks--;
966c66b8046SYuri Pankov 	else
967c66b8046SYuri Pankov 		quote_blocks--;
968c66b8046SYuri Pankov 	outflags |= MD_sp;
969c66b8046SYuri Pankov }
970c66b8046SYuri Pankov 
971c66b8046SYuri Pankov static int
md_pre_Dl(struct roff_node * n)972c66b8046SYuri Pankov md_pre_Dl(struct roff_node *n)
973c66b8046SYuri Pankov {
974c66b8046SYuri Pankov 	/*
975c66b8046SYuri Pankov 	 * Markdown code block syntax does not work inside blockquotes.
976c66b8046SYuri Pankov 	 * The best we can do is fall back to another nested blockquote.
977c66b8046SYuri Pankov 	 */
978c66b8046SYuri Pankov 	if (quote_blocks) {
979c66b8046SYuri Pankov 		md_stack('>');
980c66b8046SYuri Pankov 		quote_blocks++;
981c66b8046SYuri Pankov 	} else {
982c66b8046SYuri Pankov 		md_stack('\t');
983c66b8046SYuri Pankov 		code_blocks++;
984c66b8046SYuri Pankov 	}
985c66b8046SYuri Pankov 	outflags |= MD_sp;
986c66b8046SYuri Pankov 	return 1;
987c66b8046SYuri Pankov }
988c66b8046SYuri Pankov 
989c66b8046SYuri Pankov static int
md_pre_En(struct roff_node * n)990c66b8046SYuri Pankov md_pre_En(struct roff_node *n)
991c66b8046SYuri Pankov {
992c66b8046SYuri Pankov 	if (n->norm->Es == NULL ||
993c66b8046SYuri Pankov 	    n->norm->Es->child == NULL)
994c66b8046SYuri Pankov 		return 1;
995c66b8046SYuri Pankov 
996c66b8046SYuri Pankov 	md_word(n->norm->Es->child->string);
997c66b8046SYuri Pankov 	outflags &= ~MD_spc;
998c66b8046SYuri Pankov 	return 1;
999c66b8046SYuri Pankov }
1000c66b8046SYuri Pankov 
1001c66b8046SYuri Pankov static void
md_post_En(struct roff_node * n)1002c66b8046SYuri Pankov md_post_En(struct roff_node *n)
1003c66b8046SYuri Pankov {
1004c66b8046SYuri Pankov 	if (n->norm->Es == NULL ||
1005c66b8046SYuri Pankov 	    n->norm->Es->child == NULL ||
1006c66b8046SYuri Pankov 	    n->norm->Es->child->next == NULL)
1007c66b8046SYuri Pankov 		return;
1008c66b8046SYuri Pankov 
1009c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1010c66b8046SYuri Pankov 	md_word(n->norm->Es->child->next->string);
1011c66b8046SYuri Pankov }
1012c66b8046SYuri Pankov 
1013c66b8046SYuri Pankov static int
md_pre_Eo(struct roff_node * n)1014c66b8046SYuri Pankov md_pre_Eo(struct roff_node *n)
1015c66b8046SYuri Pankov {
1016c66b8046SYuri Pankov 	if (n->end == ENDBODY_NOT &&
1017c66b8046SYuri Pankov 	    n->parent->head->child == NULL &&
1018c66b8046SYuri Pankov 	    n->child != NULL &&
1019c66b8046SYuri Pankov 	    n->child->end != ENDBODY_NOT)
1020c66b8046SYuri Pankov 		md_preword();
1021c66b8046SYuri Pankov 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1022c66b8046SYuri Pankov 	    n->parent->head->child != NULL && (n->child != NULL ||
1023c66b8046SYuri Pankov 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1024c66b8046SYuri Pankov 		outflags &= ~(MD_spc | MD_nl);
1025c66b8046SYuri Pankov 	return 1;
1026c66b8046SYuri Pankov }
1027c66b8046SYuri Pankov 
1028c66b8046SYuri Pankov static void
md_post_Eo(struct roff_node * n)1029c66b8046SYuri Pankov md_post_Eo(struct roff_node *n)
1030c66b8046SYuri Pankov {
1031c66b8046SYuri Pankov 	if (n->end != ENDBODY_NOT) {
1032c66b8046SYuri Pankov 		outflags |= MD_spc;
1033c66b8046SYuri Pankov 		return;
1034c66b8046SYuri Pankov 	}
1035c66b8046SYuri Pankov 
1036c66b8046SYuri Pankov 	if (n->child == NULL && n->parent->head->child == NULL)
1037c66b8046SYuri Pankov 		return;
1038c66b8046SYuri Pankov 
1039c66b8046SYuri Pankov 	if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1040c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1041c66b8046SYuri Pankov         else
1042c66b8046SYuri Pankov 		outflags |= MD_spc;
1043c66b8046SYuri Pankov }
1044c66b8046SYuri Pankov 
1045c66b8046SYuri Pankov static int
md_pre_Fa(struct roff_node * n)1046c66b8046SYuri Pankov md_pre_Fa(struct roff_node *n)
1047c66b8046SYuri Pankov {
1048c66b8046SYuri Pankov 	int	 am_Fa;
1049c66b8046SYuri Pankov 
1050c66b8046SYuri Pankov 	am_Fa = n->tok == MDOC_Fa;
1051c66b8046SYuri Pankov 
1052c66b8046SYuri Pankov 	if (am_Fa)
1053c66b8046SYuri Pankov 		n = n->child;
1054c66b8046SYuri Pankov 
1055c66b8046SYuri Pankov 	while (n != NULL) {
1056c66b8046SYuri Pankov 		md_rawword("*");
1057c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1058c66b8046SYuri Pankov 		md_node(n);
1059c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1060c66b8046SYuri Pankov 		md_rawword("*");
1061c66b8046SYuri Pankov 		if ((n = n->next) != NULL)
1062c66b8046SYuri Pankov 			md_word(",");
1063c66b8046SYuri Pankov 	}
1064c66b8046SYuri Pankov 	return 0;
1065c66b8046SYuri Pankov }
1066c66b8046SYuri Pankov 
1067c66b8046SYuri Pankov static void
md_post_Fa(struct roff_node * n)1068c66b8046SYuri Pankov md_post_Fa(struct roff_node *n)
1069c66b8046SYuri Pankov {
1070*4d131170SRobert Mustacchi 	struct roff_node *nn;
1071*4d131170SRobert Mustacchi 
1072*4d131170SRobert Mustacchi 	if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa)
1073c66b8046SYuri Pankov 		md_word(",");
1074c66b8046SYuri Pankov }
1075c66b8046SYuri Pankov 
1076c66b8046SYuri Pankov static int
md_pre_Fd(struct roff_node * n)1077c66b8046SYuri Pankov md_pre_Fd(struct roff_node *n)
1078c66b8046SYuri Pankov {
1079c66b8046SYuri Pankov 	md_pre_syn(n);
1080c66b8046SYuri Pankov 	md_pre_raw(n);
1081c66b8046SYuri Pankov 	return 1;
1082c66b8046SYuri Pankov }
1083c66b8046SYuri Pankov 
1084c66b8046SYuri Pankov static void
md_post_Fd(struct roff_node * n)1085c66b8046SYuri Pankov md_post_Fd(struct roff_node *n)
1086c66b8046SYuri Pankov {
1087c66b8046SYuri Pankov 	md_post_raw(n);
1088c66b8046SYuri Pankov 	outflags |= MD_br;
1089c66b8046SYuri Pankov }
1090c66b8046SYuri Pankov 
1091c66b8046SYuri Pankov static void
md_post_Fl(struct roff_node * n)1092c66b8046SYuri Pankov md_post_Fl(struct roff_node *n)
1093c66b8046SYuri Pankov {
1094*4d131170SRobert Mustacchi 	struct roff_node *nn;
1095*4d131170SRobert Mustacchi 
1096c66b8046SYuri Pankov 	md_post_raw(n);
1097*4d131170SRobert Mustacchi 	if (n->child == NULL && (nn = roff_node_next(n)) != NULL &&
1098*4d131170SRobert Mustacchi 	    nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0)
1099c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1100c66b8046SYuri Pankov }
1101c66b8046SYuri Pankov 
1102c66b8046SYuri Pankov static int
md_pre_Fn(struct roff_node * n)1103c66b8046SYuri Pankov md_pre_Fn(struct roff_node *n)
1104c66b8046SYuri Pankov {
1105c66b8046SYuri Pankov 	md_pre_syn(n);
1106c66b8046SYuri Pankov 
1107c66b8046SYuri Pankov 	if ((n = n->child) == NULL)
1108c66b8046SYuri Pankov 		return 0;
1109c66b8046SYuri Pankov 
1110c66b8046SYuri Pankov 	md_rawword("**");
1111c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1112c66b8046SYuri Pankov 	md_node(n);
1113c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1114c66b8046SYuri Pankov 	md_rawword("**");
1115c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1116c66b8046SYuri Pankov 	md_word("(");
1117c66b8046SYuri Pankov 
1118c66b8046SYuri Pankov 	if ((n = n->next) != NULL)
1119c66b8046SYuri Pankov 		md_pre_Fa(n);
1120c66b8046SYuri Pankov 	return 0;
1121c66b8046SYuri Pankov }
1122c66b8046SYuri Pankov 
1123c66b8046SYuri Pankov static void
md_post_Fn(struct roff_node * n)1124c66b8046SYuri Pankov md_post_Fn(struct roff_node *n)
1125c66b8046SYuri Pankov {
1126c66b8046SYuri Pankov 	md_word(")");
1127c66b8046SYuri Pankov 	if (n->flags & NODE_SYNPRETTY) {
1128c66b8046SYuri Pankov 		md_word(";");
1129c66b8046SYuri Pankov 		outflags |= MD_sp;
1130c66b8046SYuri Pankov 	}
1131c66b8046SYuri Pankov }
1132c66b8046SYuri Pankov 
1133c66b8046SYuri Pankov static int
md_pre_Fo(struct roff_node * n)1134c66b8046SYuri Pankov md_pre_Fo(struct roff_node *n)
1135c66b8046SYuri Pankov {
1136c66b8046SYuri Pankov 	switch (n->type) {
1137c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1138c66b8046SYuri Pankov 		md_pre_syn(n);
1139c66b8046SYuri Pankov 		break;
1140c66b8046SYuri Pankov 	case ROFFT_HEAD:
1141c66b8046SYuri Pankov 		if (n->child == NULL)
1142c66b8046SYuri Pankov 			return 0;
1143c66b8046SYuri Pankov 		md_pre_raw(n);
1144c66b8046SYuri Pankov 		break;
1145c66b8046SYuri Pankov 	case ROFFT_BODY:
1146c66b8046SYuri Pankov 		outflags &= ~(MD_spc | MD_nl);
1147c66b8046SYuri Pankov 		md_word("(");
1148c66b8046SYuri Pankov 		break;
1149c66b8046SYuri Pankov 	default:
1150c66b8046SYuri Pankov 		break;
1151c66b8046SYuri Pankov 	}
1152c66b8046SYuri Pankov 	return 1;
1153c66b8046SYuri Pankov }
1154c66b8046SYuri Pankov 
1155c66b8046SYuri Pankov static void
md_post_Fo(struct roff_node * n)1156c66b8046SYuri Pankov md_post_Fo(struct roff_node *n)
1157c66b8046SYuri Pankov {
1158c66b8046SYuri Pankov 	switch (n->type) {
1159c66b8046SYuri Pankov 	case ROFFT_HEAD:
1160c66b8046SYuri Pankov 		if (n->child != NULL)
1161c66b8046SYuri Pankov 			md_post_raw(n);
1162c66b8046SYuri Pankov 		break;
1163c66b8046SYuri Pankov 	case ROFFT_BODY:
1164c66b8046SYuri Pankov 		md_post_Fn(n);
1165c66b8046SYuri Pankov 		break;
1166c66b8046SYuri Pankov 	default:
1167c66b8046SYuri Pankov 		break;
1168c66b8046SYuri Pankov 	}
1169c66b8046SYuri Pankov }
1170c66b8046SYuri Pankov 
1171c66b8046SYuri Pankov static int
md_pre_In(struct roff_node * n)1172c66b8046SYuri Pankov md_pre_In(struct roff_node *n)
1173c66b8046SYuri Pankov {
1174c66b8046SYuri Pankov 	if (n->flags & NODE_SYNPRETTY) {
1175c66b8046SYuri Pankov 		md_pre_syn(n);
1176c66b8046SYuri Pankov 		md_rawword("**");
1177c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1178c66b8046SYuri Pankov 		md_word("#include <");
1179c66b8046SYuri Pankov 	} else {
1180c66b8046SYuri Pankov 		md_word("<");
1181c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1182c66b8046SYuri Pankov 		md_rawword("*");
1183c66b8046SYuri Pankov 	}
1184c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1185c66b8046SYuri Pankov 	return 1;
1186c66b8046SYuri Pankov }
1187c66b8046SYuri Pankov 
1188c66b8046SYuri Pankov static void
md_post_In(struct roff_node * n)1189c66b8046SYuri Pankov md_post_In(struct roff_node *n)
1190c66b8046SYuri Pankov {
1191c66b8046SYuri Pankov 	if (n->flags & NODE_SYNPRETTY) {
1192c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1193c66b8046SYuri Pankov 		md_rawword(">**");
1194c66b8046SYuri Pankov 		outflags |= MD_nl;
1195c66b8046SYuri Pankov 	} else {
1196c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1197c66b8046SYuri Pankov 		md_rawword("*>");
1198c66b8046SYuri Pankov 	}
1199c66b8046SYuri Pankov }
1200c66b8046SYuri Pankov 
1201c66b8046SYuri Pankov static int
md_pre_It(struct roff_node * n)1202c66b8046SYuri Pankov md_pre_It(struct roff_node *n)
1203c66b8046SYuri Pankov {
1204c66b8046SYuri Pankov 	struct roff_node	*bln;
1205c66b8046SYuri Pankov 
1206c66b8046SYuri Pankov 	switch (n->type) {
1207c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1208c66b8046SYuri Pankov 		return 1;
1209c66b8046SYuri Pankov 
1210c66b8046SYuri Pankov 	case ROFFT_HEAD:
1211c66b8046SYuri Pankov 		bln = n->parent->parent;
1212c66b8046SYuri Pankov 		if (bln->norm->Bl.comp == 0 &&
1213c66b8046SYuri Pankov 		    bln->norm->Bl.type != LIST_column)
1214c66b8046SYuri Pankov 			outflags |= MD_sp;
1215c66b8046SYuri Pankov 		outflags |= MD_nl;
1216c66b8046SYuri Pankov 
1217c66b8046SYuri Pankov 		switch (bln->norm->Bl.type) {
1218c66b8046SYuri Pankov 		case LIST_item:
1219c66b8046SYuri Pankov 			outflags |= MD_br;
1220c66b8046SYuri Pankov 			return 0;
1221c66b8046SYuri Pankov 		case LIST_inset:
1222c66b8046SYuri Pankov 		case LIST_diag:
1223c66b8046SYuri Pankov 		case LIST_ohang:
1224c66b8046SYuri Pankov 			outflags |= MD_br;
1225c66b8046SYuri Pankov 			return 1;
1226c66b8046SYuri Pankov 		case LIST_tag:
1227c66b8046SYuri Pankov 		case LIST_hang:
1228c66b8046SYuri Pankov 			outflags |= MD_sp;
1229c66b8046SYuri Pankov 			return 1;
1230c66b8046SYuri Pankov 		case LIST_bullet:
1231c66b8046SYuri Pankov 			md_rawword("*\t");
1232c66b8046SYuri Pankov 			break;
1233c66b8046SYuri Pankov 		case LIST_dash:
1234c66b8046SYuri Pankov 		case LIST_hyphen:
1235c66b8046SYuri Pankov 			md_rawword("-\t");
1236c66b8046SYuri Pankov 			break;
1237c66b8046SYuri Pankov 		case LIST_enum:
1238c66b8046SYuri Pankov 			md_preword();
1239c66b8046SYuri Pankov 			if (bln->norm->Bl.count < 99)
1240c66b8046SYuri Pankov 				bln->norm->Bl.count++;
1241c66b8046SYuri Pankov 			printf("%d.\t", bln->norm->Bl.count);
1242c66b8046SYuri Pankov 			escflags &= ~ESC_FON;
1243c66b8046SYuri Pankov 			break;
1244c66b8046SYuri Pankov 		case LIST_column:
1245c66b8046SYuri Pankov 			outflags |= MD_br;
1246c66b8046SYuri Pankov 			return 0;
1247c66b8046SYuri Pankov 		default:
1248c66b8046SYuri Pankov 			return 0;
1249c66b8046SYuri Pankov 		}
1250c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1251c66b8046SYuri Pankov 		outflags |= MD_nonl;
1252c66b8046SYuri Pankov 		outcount = 0;
1253c66b8046SYuri Pankov 		md_stack('\t');
1254c66b8046SYuri Pankov 		if (code_blocks || quote_blocks)
1255c66b8046SYuri Pankov 			list_blocks++;
1256c66b8046SYuri Pankov 		return 0;
1257c66b8046SYuri Pankov 
1258c66b8046SYuri Pankov 	case ROFFT_BODY:
1259c66b8046SYuri Pankov 		bln = n->parent->parent;
1260c66b8046SYuri Pankov 		switch (bln->norm->Bl.type) {
1261c66b8046SYuri Pankov 		case LIST_ohang:
1262c66b8046SYuri Pankov 			outflags |= MD_br;
1263c66b8046SYuri Pankov 			break;
1264c66b8046SYuri Pankov 		case LIST_tag:
1265c66b8046SYuri Pankov 		case LIST_hang:
1266c66b8046SYuri Pankov 			md_pre_D1(n);
1267c66b8046SYuri Pankov 			break;
1268c66b8046SYuri Pankov 		default:
1269c66b8046SYuri Pankov 			break;
1270c66b8046SYuri Pankov 		}
1271c66b8046SYuri Pankov 		return 1;
1272c66b8046SYuri Pankov 
1273c66b8046SYuri Pankov 	default:
1274c66b8046SYuri Pankov 		return 0;
1275c66b8046SYuri Pankov 	}
1276c66b8046SYuri Pankov }
1277c66b8046SYuri Pankov 
1278c66b8046SYuri Pankov static void
md_post_It(struct roff_node * n)1279c66b8046SYuri Pankov md_post_It(struct roff_node *n)
1280c66b8046SYuri Pankov {
1281c66b8046SYuri Pankov 	struct roff_node	*bln;
1282c66b8046SYuri Pankov 	int			 i, nc;
1283c66b8046SYuri Pankov 
1284c66b8046SYuri Pankov 	if (n->type != ROFFT_BODY)
1285c66b8046SYuri Pankov 		return;
1286c66b8046SYuri Pankov 
1287c66b8046SYuri Pankov 	bln = n->parent->parent;
1288c66b8046SYuri Pankov 	switch (bln->norm->Bl.type) {
1289c66b8046SYuri Pankov 	case LIST_bullet:
1290c66b8046SYuri Pankov 	case LIST_dash:
1291c66b8046SYuri Pankov 	case LIST_hyphen:
1292c66b8046SYuri Pankov 	case LIST_enum:
1293c66b8046SYuri Pankov 		md_stack((char)-1);
1294c66b8046SYuri Pankov 		if (code_blocks || quote_blocks)
1295c66b8046SYuri Pankov 			list_blocks--;
1296c66b8046SYuri Pankov 		break;
1297c66b8046SYuri Pankov 	case LIST_tag:
1298c66b8046SYuri Pankov 	case LIST_hang:
1299c66b8046SYuri Pankov 		md_post_D1(n);
1300c66b8046SYuri Pankov 		break;
1301c66b8046SYuri Pankov 
1302c66b8046SYuri Pankov 	case LIST_column:
1303c66b8046SYuri Pankov 		if (n->next == NULL)
1304c66b8046SYuri Pankov 			break;
1305c66b8046SYuri Pankov 
1306c66b8046SYuri Pankov 		/* Calculate the array index of the current column. */
1307c66b8046SYuri Pankov 
1308c66b8046SYuri Pankov 		i = 0;
1309c66b8046SYuri Pankov 		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1310c66b8046SYuri Pankov 			i++;
1311c66b8046SYuri Pankov 
1312cec8643bSMichal Nowak 		/*
1313c66b8046SYuri Pankov 		 * If a width was specified for this column,
1314c66b8046SYuri Pankov 		 * subtract what printed, and
1315c66b8046SYuri Pankov 		 * add the same spacing as in mdoc_term.c.
1316c66b8046SYuri Pankov 		 */
1317c66b8046SYuri Pankov 
1318c66b8046SYuri Pankov 		nc = bln->norm->Bl.ncols;
1319c66b8046SYuri Pankov 		i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1320c66b8046SYuri Pankov 		    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1321c66b8046SYuri Pankov 		if (i < 1)
1322c66b8046SYuri Pankov 			i = 1;
1323c66b8046SYuri Pankov 		while (i-- > 0)
1324c66b8046SYuri Pankov 			putchar(' ');
1325c66b8046SYuri Pankov 
1326c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1327c66b8046SYuri Pankov 		escflags &= ~ESC_FON;
1328c66b8046SYuri Pankov 		outcount = 0;
1329c66b8046SYuri Pankov 		break;
1330c66b8046SYuri Pankov 
1331c66b8046SYuri Pankov 	default:
1332c66b8046SYuri Pankov 		break;
1333c66b8046SYuri Pankov 	}
1334c66b8046SYuri Pankov }
1335c66b8046SYuri Pankov 
1336c66b8046SYuri Pankov static void
md_post_Lb(struct roff_node * n)1337c66b8046SYuri Pankov md_post_Lb(struct roff_node *n)
1338c66b8046SYuri Pankov {
1339c66b8046SYuri Pankov 	if (n->sec == SEC_LIBRARY)
1340c66b8046SYuri Pankov 		outflags |= MD_br;
1341c66b8046SYuri Pankov }
1342c66b8046SYuri Pankov 
1343c66b8046SYuri Pankov static void
md_uri(const char * s)1344c66b8046SYuri Pankov md_uri(const char *s)
1345c66b8046SYuri Pankov {
1346c66b8046SYuri Pankov 	while (*s != '\0') {
1347c66b8046SYuri Pankov 		if (strchr("%()<>", *s) != NULL) {
1348c66b8046SYuri Pankov 			printf("%%%2.2hhX", *s);
1349c66b8046SYuri Pankov 			outcount += 3;
1350c66b8046SYuri Pankov 		} else {
1351c66b8046SYuri Pankov 			putchar(*s);
1352c66b8046SYuri Pankov 			outcount++;
1353c66b8046SYuri Pankov 		}
1354c66b8046SYuri Pankov 		s++;
1355c66b8046SYuri Pankov 	}
1356c66b8046SYuri Pankov }
1357c66b8046SYuri Pankov 
1358c66b8046SYuri Pankov static int
md_pre_Lk(struct roff_node * n)1359c66b8046SYuri Pankov md_pre_Lk(struct roff_node *n)
1360c66b8046SYuri Pankov {
1361c66b8046SYuri Pankov 	const struct roff_node *link, *descr, *punct;
1362c66b8046SYuri Pankov 
1363c66b8046SYuri Pankov 	if ((link = n->child) == NULL)
1364c66b8046SYuri Pankov 		return 0;
1365c66b8046SYuri Pankov 
1366c66b8046SYuri Pankov 	/* Find beginning of trailing punctuation. */
1367c66b8046SYuri Pankov 	punct = n->last;
1368c66b8046SYuri Pankov 	while (punct != link && punct->flags & NODE_DELIMC)
1369c66b8046SYuri Pankov 		punct = punct->prev;
1370c66b8046SYuri Pankov 	punct = punct->next;
1371c66b8046SYuri Pankov 
1372c66b8046SYuri Pankov 	/* Link text. */
1373c66b8046SYuri Pankov 	descr = link->next;
1374c66b8046SYuri Pankov 	if (descr == punct)
1375c66b8046SYuri Pankov 		descr = link;  /* no text */
1376c66b8046SYuri Pankov 	md_rawword("[");
1377c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1378c66b8046SYuri Pankov 	do {
1379c66b8046SYuri Pankov 		md_word(descr->string);
1380c66b8046SYuri Pankov 		descr = descr->next;
1381c66b8046SYuri Pankov 	} while (descr != punct);
1382c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1383c66b8046SYuri Pankov 
1384c66b8046SYuri Pankov 	/* Link target. */
1385c66b8046SYuri Pankov 	md_rawword("](");
1386c66b8046SYuri Pankov 	md_uri(link->string);
1387c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1388c66b8046SYuri Pankov 	md_rawword(")");
1389c66b8046SYuri Pankov 
1390c66b8046SYuri Pankov 	/* Trailing punctuation. */
1391c66b8046SYuri Pankov 	while (punct != NULL) {
1392c66b8046SYuri Pankov 		md_word(punct->string);
1393c66b8046SYuri Pankov 		punct = punct->next;
1394c66b8046SYuri Pankov 	}
1395c66b8046SYuri Pankov 	return 0;
1396c66b8046SYuri Pankov }
1397c66b8046SYuri Pankov 
1398c66b8046SYuri Pankov static int
md_pre_Mt(struct roff_node * n)1399c66b8046SYuri Pankov md_pre_Mt(struct roff_node *n)
1400c66b8046SYuri Pankov {
1401c66b8046SYuri Pankov 	const struct roff_node *nch;
1402c66b8046SYuri Pankov 
1403c66b8046SYuri Pankov 	md_rawword("[");
1404c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1405c66b8046SYuri Pankov 	for (nch = n->child; nch != NULL; nch = nch->next)
1406c66b8046SYuri Pankov 		md_word(nch->string);
1407c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1408c66b8046SYuri Pankov 	md_rawword("](mailto:");
1409c66b8046SYuri Pankov 	for (nch = n->child; nch != NULL; nch = nch->next) {
1410c66b8046SYuri Pankov 		md_uri(nch->string);
1411c66b8046SYuri Pankov 		if (nch->next != NULL) {
1412c66b8046SYuri Pankov 			putchar(' ');
1413c66b8046SYuri Pankov 			outcount++;
1414c66b8046SYuri Pankov 		}
1415c66b8046SYuri Pankov 	}
1416c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1417c66b8046SYuri Pankov 	md_rawword(")");
1418c66b8046SYuri Pankov 	return 0;
1419c66b8046SYuri Pankov }
1420c66b8046SYuri Pankov 
1421c66b8046SYuri Pankov static int
md_pre_Nd(struct roff_node * n)1422c66b8046SYuri Pankov md_pre_Nd(struct roff_node *n)
1423c66b8046SYuri Pankov {
1424c66b8046SYuri Pankov 	outflags &= ~MD_nl;
1425c66b8046SYuri Pankov 	outflags |= MD_spc;
1426c66b8046SYuri Pankov 	md_word("-");
1427c66b8046SYuri Pankov 	return 1;
1428c66b8046SYuri Pankov }
1429c66b8046SYuri Pankov 
1430c66b8046SYuri Pankov static int
md_pre_Nm(struct roff_node * n)1431c66b8046SYuri Pankov md_pre_Nm(struct roff_node *n)
1432c66b8046SYuri Pankov {
1433c66b8046SYuri Pankov 	switch (n->type) {
1434c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1435c66b8046SYuri Pankov 		outflags |= MD_Bk;
1436c66b8046SYuri Pankov 		md_pre_syn(n);
1437c66b8046SYuri Pankov 		break;
1438c66b8046SYuri Pankov 	case ROFFT_HEAD:
1439c66b8046SYuri Pankov 	case ROFFT_ELEM:
1440c66b8046SYuri Pankov 		md_pre_raw(n);
1441c66b8046SYuri Pankov 		break;
1442c66b8046SYuri Pankov 	default:
1443c66b8046SYuri Pankov 		break;
1444c66b8046SYuri Pankov 	}
1445c66b8046SYuri Pankov 	return 1;
1446c66b8046SYuri Pankov }
1447c66b8046SYuri Pankov 
1448c66b8046SYuri Pankov static void
md_post_Nm(struct roff_node * n)1449c66b8046SYuri Pankov md_post_Nm(struct roff_node *n)
1450c66b8046SYuri Pankov {
1451c66b8046SYuri Pankov 	switch (n->type) {
1452c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1453c66b8046SYuri Pankov 		outflags &= ~MD_Bk;
1454c66b8046SYuri Pankov 		break;
1455c66b8046SYuri Pankov 	case ROFFT_HEAD:
1456c66b8046SYuri Pankov 	case ROFFT_ELEM:
1457c66b8046SYuri Pankov 		md_post_raw(n);
1458c66b8046SYuri Pankov 		break;
1459c66b8046SYuri Pankov 	default:
1460c66b8046SYuri Pankov 		break;
1461c66b8046SYuri Pankov 	}
1462c66b8046SYuri Pankov }
1463c66b8046SYuri Pankov 
1464c66b8046SYuri Pankov static int
md_pre_No(struct roff_node * n)1465c66b8046SYuri Pankov md_pre_No(struct roff_node *n)
1466c66b8046SYuri Pankov {
1467c66b8046SYuri Pankov 	outflags |= MD_spc_force;
1468c66b8046SYuri Pankov 	return 1;
1469c66b8046SYuri Pankov }
1470c66b8046SYuri Pankov 
1471c66b8046SYuri Pankov static int
md_pre_Ns(struct roff_node * n)1472c66b8046SYuri Pankov md_pre_Ns(struct roff_node *n)
1473c66b8046SYuri Pankov {
1474c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1475c66b8046SYuri Pankov 	return 0;
1476c66b8046SYuri Pankov }
1477c66b8046SYuri Pankov 
1478c66b8046SYuri Pankov static void
md_post_Pf(struct roff_node * n)1479c66b8046SYuri Pankov md_post_Pf(struct roff_node *n)
1480c66b8046SYuri Pankov {
1481c66b8046SYuri Pankov 	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1482c66b8046SYuri Pankov 		outflags &= ~MD_spc;
1483c66b8046SYuri Pankov }
1484c66b8046SYuri Pankov 
1485c66b8046SYuri Pankov static int
md_pre_Pp(struct roff_node * n)1486c66b8046SYuri Pankov md_pre_Pp(struct roff_node *n)
1487c66b8046SYuri Pankov {
1488c66b8046SYuri Pankov 	outflags |= MD_sp;
1489c66b8046SYuri Pankov 	return 0;
1490c66b8046SYuri Pankov }
1491c66b8046SYuri Pankov 
1492c66b8046SYuri Pankov static int
md_pre_Rs(struct roff_node * n)1493c66b8046SYuri Pankov md_pre_Rs(struct roff_node *n)
1494c66b8046SYuri Pankov {
1495c66b8046SYuri Pankov 	if (n->sec == SEC_SEE_ALSO)
1496c66b8046SYuri Pankov 		outflags |= MD_sp;
1497c66b8046SYuri Pankov 	return 1;
1498c66b8046SYuri Pankov }
1499c66b8046SYuri Pankov 
1500c66b8046SYuri Pankov static int
md_pre_Sh(struct roff_node * n)1501c66b8046SYuri Pankov md_pre_Sh(struct roff_node *n)
1502c66b8046SYuri Pankov {
1503c66b8046SYuri Pankov 	switch (n->type) {
1504c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1505c66b8046SYuri Pankov 		if (n->sec == SEC_AUTHORS)
1506c66b8046SYuri Pankov 			outflags &= ~(MD_An_split | MD_An_nosplit);
1507c66b8046SYuri Pankov 		break;
1508c66b8046SYuri Pankov 	case ROFFT_HEAD:
1509c66b8046SYuri Pankov 		outflags |= MD_sp;
1510c66b8046SYuri Pankov 		md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1511c66b8046SYuri Pankov 		break;
1512c66b8046SYuri Pankov 	case ROFFT_BODY:
1513c66b8046SYuri Pankov 		outflags |= MD_sp;
1514c66b8046SYuri Pankov 		break;
1515c66b8046SYuri Pankov 	default:
1516c66b8046SYuri Pankov 		break;
1517c66b8046SYuri Pankov 	}
1518c66b8046SYuri Pankov 	return 1;
1519c66b8046SYuri Pankov }
1520c66b8046SYuri Pankov 
1521c66b8046SYuri Pankov static int
md_pre_Sm(struct roff_node * n)1522c66b8046SYuri Pankov md_pre_Sm(struct roff_node *n)
1523c66b8046SYuri Pankov {
1524c66b8046SYuri Pankov 	if (n->child == NULL)
1525c66b8046SYuri Pankov 		outflags ^= MD_Sm;
1526c66b8046SYuri Pankov 	else if (strcmp("on", n->child->string) == 0)
1527c66b8046SYuri Pankov 		outflags |= MD_Sm;
1528c66b8046SYuri Pankov 	else
1529c66b8046SYuri Pankov 		outflags &= ~MD_Sm;
1530c66b8046SYuri Pankov 
1531c66b8046SYuri Pankov 	if (outflags & MD_Sm)
1532c66b8046SYuri Pankov 		outflags |= MD_spc;
1533c66b8046SYuri Pankov 
1534c66b8046SYuri Pankov 	return 0;
1535c66b8046SYuri Pankov }
1536c66b8046SYuri Pankov 
1537c66b8046SYuri Pankov static int
md_pre_Vt(struct roff_node * n)1538c66b8046SYuri Pankov md_pre_Vt(struct roff_node *n)
1539c66b8046SYuri Pankov {
1540c66b8046SYuri Pankov 	switch (n->type) {
1541c66b8046SYuri Pankov 	case ROFFT_BLOCK:
1542c66b8046SYuri Pankov 		md_pre_syn(n);
1543c66b8046SYuri Pankov 		return 1;
1544c66b8046SYuri Pankov 	case ROFFT_BODY:
1545c66b8046SYuri Pankov 	case ROFFT_ELEM:
1546c66b8046SYuri Pankov 		md_pre_raw(n);
1547c66b8046SYuri Pankov 		return 1;
1548c66b8046SYuri Pankov 	default:
1549c66b8046SYuri Pankov 		return 0;
1550c66b8046SYuri Pankov 	}
1551c66b8046SYuri Pankov }
1552c66b8046SYuri Pankov 
1553c66b8046SYuri Pankov static void
md_post_Vt(struct roff_node * n)1554c66b8046SYuri Pankov md_post_Vt(struct roff_node *n)
1555c66b8046SYuri Pankov {
1556c66b8046SYuri Pankov 	switch (n->type) {
1557c66b8046SYuri Pankov 	case ROFFT_BODY:
1558c66b8046SYuri Pankov 	case ROFFT_ELEM:
1559c66b8046SYuri Pankov 		md_post_raw(n);
1560c66b8046SYuri Pankov 		break;
1561c66b8046SYuri Pankov 	default:
1562c66b8046SYuri Pankov 		break;
1563c66b8046SYuri Pankov 	}
1564c66b8046SYuri Pankov }
1565c66b8046SYuri Pankov 
1566c66b8046SYuri Pankov static int
md_pre_Xr(struct roff_node * n)1567c66b8046SYuri Pankov md_pre_Xr(struct roff_node *n)
1568c66b8046SYuri Pankov {
1569c66b8046SYuri Pankov 	n = n->child;
1570c66b8046SYuri Pankov 	if (n == NULL)
1571c66b8046SYuri Pankov 		return 0;
1572c66b8046SYuri Pankov 	md_node(n);
1573c66b8046SYuri Pankov 	n = n->next;
1574c66b8046SYuri Pankov 	if (n == NULL)
1575c66b8046SYuri Pankov 		return 0;
1576c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1577c66b8046SYuri Pankov 	md_word("(");
1578c66b8046SYuri Pankov 	md_node(n);
1579c66b8046SYuri Pankov 	md_word(")");
1580c66b8046SYuri Pankov 	return 0;
1581c66b8046SYuri Pankov }
1582c66b8046SYuri Pankov 
1583c66b8046SYuri Pankov static int
md_pre__T(struct roff_node * n)1584c66b8046SYuri Pankov md_pre__T(struct roff_node *n)
1585c66b8046SYuri Pankov {
1586c66b8046SYuri Pankov 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1587c66b8046SYuri Pankov 		md_word("\"");
1588c66b8046SYuri Pankov 	else
1589c66b8046SYuri Pankov 		md_rawword("*");
1590c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1591c66b8046SYuri Pankov 	return 1;
1592c66b8046SYuri Pankov }
1593c66b8046SYuri Pankov 
1594c66b8046SYuri Pankov static void
md_post__T(struct roff_node * n)1595c66b8046SYuri Pankov md_post__T(struct roff_node *n)
1596c66b8046SYuri Pankov {
1597c66b8046SYuri Pankov 	outflags &= ~MD_spc;
1598c66b8046SYuri Pankov 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1599c66b8046SYuri Pankov 		md_word("\"");
1600c66b8046SYuri Pankov 	else
1601c66b8046SYuri Pankov 		md_rawword("*");
1602c66b8046SYuri Pankov 	md_post_pc(n);
1603c66b8046SYuri Pankov }
1604c66b8046SYuri Pankov 
1605c66b8046SYuri Pankov static int
md_pre_br(struct roff_node * n)1606c66b8046SYuri Pankov md_pre_br(struct roff_node *n)
1607c66b8046SYuri Pankov {
1608c66b8046SYuri Pankov 	outflags |= MD_br;
1609c66b8046SYuri Pankov 	return 0;
1610c66b8046SYuri Pankov }
1611