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