xref: /dflybsd-src/contrib/mdocml/mdoc_markdown.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1*99db7d0eSSascha Wildner /* $Id: mdoc_markdown.c,v 1.37 2021/08/10 12:55:03 schwarze Exp $ */
254ba9607SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org>
454ba9607SSascha Wildner  *
554ba9607SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
654ba9607SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
754ba9607SSascha Wildner  * copyright notice and this permission notice appear in all copies.
854ba9607SSascha Wildner  *
954ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1054ba9607SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1154ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1254ba9607SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1354ba9607SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1454ba9607SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1554ba9607SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*99db7d0eSSascha Wildner  *
17*99db7d0eSSascha Wildner  * Markdown formatter for mdoc(7) used by mandoc(1).
1854ba9607SSascha Wildner  */
19*99db7d0eSSascha Wildner #include "config.h"
20*99db7d0eSSascha Wildner 
2154ba9607SSascha Wildner #include <sys/types.h>
2254ba9607SSascha Wildner 
2354ba9607SSascha Wildner #include <assert.h>
2454ba9607SSascha Wildner #include <ctype.h>
2554ba9607SSascha Wildner #include <stdio.h>
2654ba9607SSascha Wildner #include <stdlib.h>
2754ba9607SSascha Wildner #include <string.h>
2854ba9607SSascha Wildner 
2954ba9607SSascha Wildner #include "mandoc_aux.h"
3054ba9607SSascha Wildner #include "mandoc.h"
3154ba9607SSascha Wildner #include "roff.h"
3254ba9607SSascha Wildner #include "mdoc.h"
3354ba9607SSascha Wildner #include "main.h"
3454ba9607SSascha Wildner 
3554ba9607SSascha Wildner struct	md_act {
36*99db7d0eSSascha Wildner 	int		(*cond)(struct roff_node *);
37*99db7d0eSSascha Wildner 	int		(*pre)(struct roff_node *);
38*99db7d0eSSascha Wildner 	void		(*post)(struct roff_node *);
3954ba9607SSascha Wildner 	const char	 *prefix; /* pre-node string constant */
4054ba9607SSascha Wildner 	const char	 *suffix; /* post-node string constant */
4154ba9607SSascha Wildner };
4254ba9607SSascha Wildner 
4354ba9607SSascha Wildner static	void	 md_nodelist(struct roff_node *);
4454ba9607SSascha Wildner static	void	 md_node(struct roff_node *);
45*99db7d0eSSascha Wildner static	const char *md_stack(char);
4654ba9607SSascha Wildner static	void	 md_preword(void);
4754ba9607SSascha Wildner static	void	 md_rawword(const char *);
4854ba9607SSascha Wildner static	void	 md_word(const char *);
4954ba9607SSascha Wildner static	void	 md_named(const char *);
5054ba9607SSascha Wildner static	void	 md_char(unsigned char);
5154ba9607SSascha Wildner static	void	 md_uri(const char *);
5254ba9607SSascha Wildner 
5354ba9607SSascha Wildner static	int	 md_cond_head(struct roff_node *);
5454ba9607SSascha Wildner static	int	 md_cond_body(struct roff_node *);
5554ba9607SSascha Wildner 
5654ba9607SSascha Wildner static	int	 md_pre_abort(struct roff_node *);
5754ba9607SSascha Wildner static	int	 md_pre_raw(struct roff_node *);
5854ba9607SSascha Wildner static	int	 md_pre_word(struct roff_node *);
5954ba9607SSascha Wildner static	int	 md_pre_skip(struct roff_node *);
6054ba9607SSascha Wildner static	void	 md_pre_syn(struct roff_node *);
6154ba9607SSascha Wildner static	int	 md_pre_An(struct roff_node *);
6254ba9607SSascha Wildner static	int	 md_pre_Ap(struct roff_node *);
6354ba9607SSascha Wildner static	int	 md_pre_Bd(struct roff_node *);
6454ba9607SSascha Wildner static	int	 md_pre_Bk(struct roff_node *);
6554ba9607SSascha Wildner static	int	 md_pre_Bl(struct roff_node *);
6654ba9607SSascha Wildner static	int	 md_pre_D1(struct roff_node *);
6754ba9607SSascha Wildner static	int	 md_pre_Dl(struct roff_node *);
6854ba9607SSascha Wildner static	int	 md_pre_En(struct roff_node *);
6954ba9607SSascha Wildner static	int	 md_pre_Eo(struct roff_node *);
7054ba9607SSascha Wildner static	int	 md_pre_Fa(struct roff_node *);
7154ba9607SSascha Wildner static	int	 md_pre_Fd(struct roff_node *);
7254ba9607SSascha Wildner static	int	 md_pre_Fn(struct roff_node *);
7354ba9607SSascha Wildner static	int	 md_pre_Fo(struct roff_node *);
7454ba9607SSascha Wildner static	int	 md_pre_In(struct roff_node *);
7554ba9607SSascha Wildner static	int	 md_pre_It(struct roff_node *);
7654ba9607SSascha Wildner static	int	 md_pre_Lk(struct roff_node *);
7754ba9607SSascha Wildner static	int	 md_pre_Mt(struct roff_node *);
7854ba9607SSascha Wildner static	int	 md_pre_Nd(struct roff_node *);
7954ba9607SSascha Wildner static	int	 md_pre_Nm(struct roff_node *);
8054ba9607SSascha Wildner static	int	 md_pre_No(struct roff_node *);
8154ba9607SSascha Wildner static	int	 md_pre_Ns(struct roff_node *);
8254ba9607SSascha Wildner static	int	 md_pre_Pp(struct roff_node *);
8354ba9607SSascha Wildner static	int	 md_pre_Rs(struct roff_node *);
8454ba9607SSascha Wildner static	int	 md_pre_Sh(struct roff_node *);
8554ba9607SSascha Wildner static	int	 md_pre_Sm(struct roff_node *);
8654ba9607SSascha Wildner static	int	 md_pre_Vt(struct roff_node *);
8754ba9607SSascha Wildner static	int	 md_pre_Xr(struct roff_node *);
8854ba9607SSascha Wildner static	int	 md_pre__T(struct roff_node *);
8954ba9607SSascha Wildner static	int	 md_pre_br(struct roff_node *);
9054ba9607SSascha Wildner 
9154ba9607SSascha Wildner static	void	 md_post_raw(struct roff_node *);
9254ba9607SSascha Wildner static	void	 md_post_word(struct roff_node *);
9354ba9607SSascha Wildner static	void	 md_post_pc(struct roff_node *);
9454ba9607SSascha Wildner static	void	 md_post_Bk(struct roff_node *);
9554ba9607SSascha Wildner static	void	 md_post_Bl(struct roff_node *);
9654ba9607SSascha Wildner static	void	 md_post_D1(struct roff_node *);
9754ba9607SSascha Wildner static	void	 md_post_En(struct roff_node *);
9854ba9607SSascha Wildner static	void	 md_post_Eo(struct roff_node *);
9954ba9607SSascha Wildner static	void	 md_post_Fa(struct roff_node *);
10054ba9607SSascha Wildner static	void	 md_post_Fd(struct roff_node *);
10154ba9607SSascha Wildner static	void	 md_post_Fl(struct roff_node *);
10254ba9607SSascha Wildner static	void	 md_post_Fn(struct roff_node *);
10354ba9607SSascha Wildner static	void	 md_post_Fo(struct roff_node *);
10454ba9607SSascha Wildner static	void	 md_post_In(struct roff_node *);
10554ba9607SSascha Wildner static	void	 md_post_It(struct roff_node *);
10654ba9607SSascha Wildner static	void	 md_post_Lb(struct roff_node *);
10754ba9607SSascha Wildner static	void	 md_post_Nm(struct roff_node *);
10854ba9607SSascha Wildner static	void	 md_post_Pf(struct roff_node *);
10954ba9607SSascha Wildner static	void	 md_post_Vt(struct roff_node *);
11054ba9607SSascha Wildner static	void	 md_post__T(struct roff_node *);
11154ba9607SSascha Wildner 
11254ba9607SSascha Wildner static	const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = {
11354ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
11454ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
11554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
11654ba9607SSascha Wildner 	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
11754ba9607SSascha Wildner 	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
11854ba9607SSascha Wildner 	{ NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
11954ba9607SSascha Wildner 	{ md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
12054ba9607SSascha Wildner 	{ md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
12154ba9607SSascha Wildner 	{ md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
12254ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
12354ba9607SSascha Wildner 	{ md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
12454ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
12554ba9607SSascha Wildner 	{ NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
12654ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
12754ba9607SSascha Wildner 	{ NULL, md_pre_An, NULL, NULL, NULL }, /* An */
12854ba9607SSascha Wildner 	{ NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
12954ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
13054ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
13154ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
13254ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
13354ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
13454ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
13554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ex */
13654ba9607SSascha Wildner 	{ NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
13754ba9607SSascha Wildner 	{ NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
13854ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
13954ba9607SSascha Wildner 	{ NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
14054ba9607SSascha Wildner 	{ NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
14154ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
14254ba9607SSascha Wildner 	{ NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
14354ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
14454ba9607SSascha Wildner 	{ md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
14554ba9607SSascha Wildner 	{ NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
14654ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
14754ba9607SSascha Wildner 	{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */
14854ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
14954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Rv */
15054ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
15154ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
15254ba9607SSascha Wildner 	{ NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
15354ba9607SSascha Wildner 	{ NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
15454ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
15554ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
15654ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
15754ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
15854ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
15954ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
16054ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
16154ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
16254ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
16354ba9607SSascha Wildner 	{ NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
16454ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
16554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
16654ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
16754ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
16854ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
16954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
17054ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
17154ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
17254ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
17354ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Bsx */
17454ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Bx */
17554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
17654ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
17754ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
17854ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
17954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
18054ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
18154ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
18254ba9607SSascha Wildner 	{ md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
18354ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Fx */
18454ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
18554ba9607SSascha Wildner 	{ NULL, md_pre_No, NULL, NULL, NULL }, /* No */
18654ba9607SSascha Wildner 	{ NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
18754ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Nx */
18854ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ox */
18954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
19054ba9607SSascha Wildner 	{ NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
19154ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
19254ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
19354ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
19454ba9607SSascha Wildner 	{ md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
19554ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
19654ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
19754ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
19854ba9607SSascha Wildner 	{ md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
19954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
20054ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
20154ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
20254ba9607SSascha Wildner 	{ NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
20354ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
20454ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
20554ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
20654ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ux */
20754ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
20854ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
20954ba9607SSascha Wildner 	{ NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
21054ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
21154ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
21254ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
21354ba9607SSascha Wildner 	{ NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
21454ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
21554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Bt */
21654ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
21754ba9607SSascha Wildner 	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
21854ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
21954ba9607SSascha Wildner 	{ NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
22054ba9607SSascha Wildner 	{ NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */
22154ba9607SSascha Wildner 	{ NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
22254ba9607SSascha Wildner 	{ NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
22354ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
22454ba9607SSascha Wildner 	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
22554ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
22654ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
22754ba9607SSascha Wildner 	{ NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
22854ba9607SSascha Wildner 	{ md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
22954ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Dx */
23054ba9607SSascha Wildner 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
23154ba9607SSascha Wildner 	{ NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
23254ba9607SSascha Wildner 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
233*99db7d0eSSascha Wildner 	{ NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */
23454ba9607SSascha Wildner };
23554ba9607SSascha Wildner static const struct md_act *md_act(enum roff_tok);
23654ba9607SSascha Wildner 
23754ba9607SSascha Wildner static	int	 outflags;
23854ba9607SSascha Wildner #define	MD_spc		 (1 << 0)  /* Blank character before next word. */
23954ba9607SSascha Wildner #define	MD_spc_force	 (1 << 1)  /* Even before trailing punctuation. */
24054ba9607SSascha Wildner #define	MD_nonl		 (1 << 2)  /* Prevent linebreak in markdown code. */
24154ba9607SSascha Wildner #define	MD_nl		 (1 << 3)  /* Break markdown code line. */
24254ba9607SSascha Wildner #define	MD_br		 (1 << 4)  /* Insert an output line break. */
24354ba9607SSascha Wildner #define	MD_sp		 (1 << 5)  /* Insert a paragraph break. */
24454ba9607SSascha Wildner #define	MD_Sm		 (1 << 6)  /* Horizontal spacing mode. */
24554ba9607SSascha Wildner #define	MD_Bk		 (1 << 7)  /* Word keep mode. */
24654ba9607SSascha Wildner #define	MD_An_split	 (1 << 8)  /* Author mode is "split". */
24754ba9607SSascha Wildner #define	MD_An_nosplit	 (1 << 9)  /* Author mode is "nosplit". */
24854ba9607SSascha Wildner 
24954ba9607SSascha Wildner static	int	 escflags; /* Escape in generated markdown code: */
25054ba9607SSascha Wildner #define	ESC_BOL	 (1 << 0)  /* "#*+-" near the beginning of a line. */
25154ba9607SSascha Wildner #define	ESC_NUM	 (1 << 1)  /* "." after a leading number. */
25254ba9607SSascha Wildner #define	ESC_HYP	 (1 << 2)  /* "(" immediately after "]". */
25354ba9607SSascha Wildner #define	ESC_SQU	 (1 << 4)  /* "]" when "[" is open. */
25454ba9607SSascha Wildner #define	ESC_FON	 (1 << 5)  /* "*" immediately after unrelated "*". */
25554ba9607SSascha Wildner #define	ESC_EOL	 (1 << 6)  /* " " at the and of a line. */
25654ba9607SSascha Wildner 
25754ba9607SSascha Wildner static	int	 code_blocks, quote_blocks, list_blocks;
25854ba9607SSascha Wildner static	int	 outcount;
25954ba9607SSascha Wildner 
26054ba9607SSascha Wildner 
26154ba9607SSascha Wildner static const struct md_act *
md_act(enum roff_tok tok)26254ba9607SSascha Wildner md_act(enum roff_tok tok)
26354ba9607SSascha Wildner {
26454ba9607SSascha Wildner 	assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
26554ba9607SSascha Wildner 	return md_acts + (tok - MDOC_Dd);
26654ba9607SSascha Wildner }
26754ba9607SSascha Wildner 
26854ba9607SSascha Wildner void
markdown_mdoc(void * arg,const struct roff_meta * mdoc)26954ba9607SSascha Wildner markdown_mdoc(void *arg, const struct roff_meta *mdoc)
27054ba9607SSascha Wildner {
27154ba9607SSascha Wildner 	outflags = MD_Sm;
27254ba9607SSascha Wildner 	md_word(mdoc->title);
27354ba9607SSascha Wildner 	if (mdoc->msec != NULL) {
27454ba9607SSascha Wildner 		outflags &= ~MD_spc;
27554ba9607SSascha Wildner 		md_word("(");
27654ba9607SSascha Wildner 		md_word(mdoc->msec);
27754ba9607SSascha Wildner 		md_word(")");
27854ba9607SSascha Wildner 	}
27954ba9607SSascha Wildner 	md_word("-");
28054ba9607SSascha Wildner 	md_word(mdoc->vol);
28154ba9607SSascha Wildner 	if (mdoc->arch != NULL) {
28254ba9607SSascha Wildner 		md_word("(");
28354ba9607SSascha Wildner 		md_word(mdoc->arch);
28454ba9607SSascha Wildner 		md_word(")");
28554ba9607SSascha Wildner 	}
28654ba9607SSascha Wildner 	outflags |= MD_sp;
28754ba9607SSascha Wildner 
28854ba9607SSascha Wildner 	md_nodelist(mdoc->first->child);
28954ba9607SSascha Wildner 
29054ba9607SSascha Wildner 	outflags |= MD_sp;
29154ba9607SSascha Wildner 	md_word(mdoc->os);
29254ba9607SSascha Wildner 	md_word("-");
29354ba9607SSascha Wildner 	md_word(mdoc->date);
29454ba9607SSascha Wildner 	putchar('\n');
29554ba9607SSascha Wildner }
29654ba9607SSascha Wildner 
29754ba9607SSascha Wildner static void
md_nodelist(struct roff_node * n)29854ba9607SSascha Wildner md_nodelist(struct roff_node *n)
29954ba9607SSascha Wildner {
30054ba9607SSascha Wildner 	while (n != NULL) {
30154ba9607SSascha Wildner 		md_node(n);
30254ba9607SSascha Wildner 		n = n->next;
30354ba9607SSascha Wildner 	}
30454ba9607SSascha Wildner }
30554ba9607SSascha Wildner 
30654ba9607SSascha Wildner static void
md_node(struct roff_node * n)30754ba9607SSascha Wildner md_node(struct roff_node *n)
30854ba9607SSascha Wildner {
30954ba9607SSascha Wildner 	const struct md_act	*act;
31054ba9607SSascha Wildner 	int			 cond, process_children;
31154ba9607SSascha Wildner 
31254ba9607SSascha Wildner 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
31354ba9607SSascha Wildner 		return;
31454ba9607SSascha Wildner 
31554ba9607SSascha Wildner 	if (outflags & MD_nonl)
31654ba9607SSascha Wildner 		outflags &= ~(MD_nl | MD_sp);
317*99db7d0eSSascha Wildner 	else if (outflags & MD_spc &&
318*99db7d0eSSascha Wildner 	     n->flags & NODE_LINE &&
319*99db7d0eSSascha Wildner 	     !roff_node_transparent(n))
32054ba9607SSascha Wildner 		outflags |= MD_nl;
32154ba9607SSascha Wildner 
32254ba9607SSascha Wildner 	act = NULL;
32354ba9607SSascha Wildner 	cond = 0;
32454ba9607SSascha Wildner 	process_children = 1;
32554ba9607SSascha Wildner 	n->flags &= ~NODE_ENDED;
32654ba9607SSascha Wildner 
32754ba9607SSascha Wildner 	if (n->type == ROFFT_TEXT) {
32854ba9607SSascha Wildner 		if (n->flags & NODE_DELIMC)
32954ba9607SSascha Wildner 			outflags &= ~(MD_spc | MD_spc_force);
33054ba9607SSascha Wildner 		else if (outflags & MD_Sm)
33154ba9607SSascha Wildner 			outflags |= MD_spc_force;
33254ba9607SSascha Wildner 		md_word(n->string);
33354ba9607SSascha Wildner 		if (n->flags & NODE_DELIMO)
33454ba9607SSascha Wildner 			outflags &= ~(MD_spc | MD_spc_force);
33554ba9607SSascha Wildner 		else if (outflags & MD_Sm)
33654ba9607SSascha Wildner 			outflags |= MD_spc;
33754ba9607SSascha Wildner 	} else if (n->tok < ROFF_MAX) {
33854ba9607SSascha Wildner 		switch (n->tok) {
33954ba9607SSascha Wildner 		case ROFF_br:
34054ba9607SSascha Wildner 			process_children = md_pre_br(n);
34154ba9607SSascha Wildner 			break;
34254ba9607SSascha Wildner 		case ROFF_sp:
34354ba9607SSascha Wildner 			process_children = md_pre_Pp(n);
34454ba9607SSascha Wildner 			break;
34554ba9607SSascha Wildner 		default:
34654ba9607SSascha Wildner 			process_children = 0;
34754ba9607SSascha Wildner 			break;
34854ba9607SSascha Wildner 		}
34954ba9607SSascha Wildner 	} else {
35054ba9607SSascha Wildner 		act = md_act(n->tok);
35154ba9607SSascha Wildner 		cond = act->cond == NULL || (*act->cond)(n);
35254ba9607SSascha Wildner 		if (cond && act->pre != NULL &&
35354ba9607SSascha Wildner 		    (n->end == ENDBODY_NOT || n->child != NULL))
35454ba9607SSascha Wildner 			process_children = (*act->pre)(n);
35554ba9607SSascha Wildner 	}
35654ba9607SSascha Wildner 
35754ba9607SSascha Wildner 	if (process_children && n->child != NULL)
35854ba9607SSascha Wildner 		md_nodelist(n->child);
35954ba9607SSascha Wildner 
36054ba9607SSascha Wildner 	if (n->flags & NODE_ENDED)
36154ba9607SSascha Wildner 		return;
36254ba9607SSascha Wildner 
36354ba9607SSascha Wildner 	if (cond && act->post != NULL)
36454ba9607SSascha Wildner 		(*act->post)(n);
36554ba9607SSascha Wildner 
36654ba9607SSascha Wildner 	if (n->end != ENDBODY_NOT)
36754ba9607SSascha Wildner 		n->body->flags |= NODE_ENDED;
36854ba9607SSascha Wildner }
36954ba9607SSascha Wildner 
37054ba9607SSascha Wildner static const char *
md_stack(char c)37154ba9607SSascha Wildner md_stack(char c)
37254ba9607SSascha Wildner {
37354ba9607SSascha Wildner 	static char	*stack;
37454ba9607SSascha Wildner 	static size_t	 sz;
37554ba9607SSascha Wildner 	static size_t	 cur;
37654ba9607SSascha Wildner 
37754ba9607SSascha Wildner 	switch (c) {
37854ba9607SSascha Wildner 	case '\0':
37954ba9607SSascha Wildner 		break;
38054ba9607SSascha Wildner 	case (char)-1:
38154ba9607SSascha Wildner 		assert(cur);
38254ba9607SSascha Wildner 		stack[--cur] = '\0';
38354ba9607SSascha Wildner 		break;
38454ba9607SSascha Wildner 	default:
38554ba9607SSascha Wildner 		if (cur + 1 >= sz) {
38654ba9607SSascha Wildner 			sz += 8;
38754ba9607SSascha Wildner 			stack = mandoc_realloc(stack, sz);
38854ba9607SSascha Wildner 		}
38954ba9607SSascha Wildner 		stack[cur] = c;
39054ba9607SSascha Wildner 		stack[++cur] = '\0';
39154ba9607SSascha Wildner 		break;
39254ba9607SSascha Wildner 	}
39354ba9607SSascha Wildner 	return stack == NULL ? "" : stack;
39454ba9607SSascha Wildner }
39554ba9607SSascha Wildner 
39654ba9607SSascha Wildner /*
39754ba9607SSascha Wildner  * Handle vertical and horizontal spacing.
39854ba9607SSascha Wildner  */
39954ba9607SSascha Wildner static void
md_preword(void)40054ba9607SSascha Wildner md_preword(void)
40154ba9607SSascha Wildner {
40254ba9607SSascha Wildner 	const char	*cp;
40354ba9607SSascha Wildner 
40454ba9607SSascha Wildner 	/*
40554ba9607SSascha Wildner 	 * If a list block is nested inside a code block or a blockquote,
40654ba9607SSascha Wildner 	 * blank lines for paragraph breaks no longer work; instead,
40754ba9607SSascha Wildner 	 * they terminate the list.  Work around this markdown issue
40854ba9607SSascha Wildner 	 * by using mere line breaks instead.
40954ba9607SSascha Wildner 	 */
41054ba9607SSascha Wildner 
41154ba9607SSascha Wildner 	if (list_blocks && outflags & MD_sp) {
41254ba9607SSascha Wildner 		outflags &= ~MD_sp;
41354ba9607SSascha Wildner 		outflags |= MD_br;
41454ba9607SSascha Wildner 	}
41554ba9607SSascha Wildner 
41654ba9607SSascha Wildner 	/*
41754ba9607SSascha Wildner 	 * End the old line if requested.
41854ba9607SSascha Wildner 	 * Escape whitespace at the end of the markdown line
41954ba9607SSascha Wildner 	 * such that it won't look like an output line break.
42054ba9607SSascha Wildner 	 */
42154ba9607SSascha Wildner 
42254ba9607SSascha Wildner 	if (outflags & MD_sp)
42354ba9607SSascha Wildner 		putchar('\n');
42454ba9607SSascha Wildner 	else if (outflags & MD_br) {
42554ba9607SSascha Wildner 		putchar(' ');
42654ba9607SSascha Wildner 		putchar(' ');
42754ba9607SSascha Wildner 	} else if (outflags & MD_nl && escflags & ESC_EOL)
42854ba9607SSascha Wildner 		md_named("zwnj");
42954ba9607SSascha Wildner 
43054ba9607SSascha Wildner 	/* Start a new line if necessary. */
43154ba9607SSascha Wildner 
43254ba9607SSascha Wildner 	if (outflags & (MD_nl | MD_br | MD_sp)) {
43354ba9607SSascha Wildner 		putchar('\n');
43454ba9607SSascha Wildner 		for (cp = md_stack('\0'); *cp != '\0'; cp++) {
43554ba9607SSascha Wildner 			putchar(*cp);
43654ba9607SSascha Wildner 			if (*cp == '>')
43754ba9607SSascha Wildner 				putchar(' ');
43854ba9607SSascha Wildner 		}
43954ba9607SSascha Wildner 		outflags &= ~(MD_nl | MD_br | MD_sp);
44054ba9607SSascha Wildner 		escflags = ESC_BOL;
44154ba9607SSascha Wildner 		outcount = 0;
44254ba9607SSascha Wildner 
44354ba9607SSascha Wildner 	/* Handle horizontal spacing. */
44454ba9607SSascha Wildner 
44554ba9607SSascha Wildner 	} else if (outflags & MD_spc) {
44654ba9607SSascha Wildner 		if (outflags & MD_Bk)
44754ba9607SSascha Wildner 			fputs("&nbsp;", stdout);
44854ba9607SSascha Wildner 		else
44954ba9607SSascha Wildner 			putchar(' ');
45054ba9607SSascha Wildner 		escflags &= ~ESC_FON;
45154ba9607SSascha Wildner 		outcount++;
45254ba9607SSascha Wildner 	}
45354ba9607SSascha Wildner 
45454ba9607SSascha Wildner 	outflags &= ~(MD_spc_force | MD_nonl);
45554ba9607SSascha Wildner 	if (outflags & MD_Sm)
45654ba9607SSascha Wildner 		outflags |= MD_spc;
45754ba9607SSascha Wildner 	else
45854ba9607SSascha Wildner 		outflags &= ~MD_spc;
45954ba9607SSascha Wildner }
46054ba9607SSascha Wildner 
46154ba9607SSascha Wildner /*
46254ba9607SSascha Wildner  * Print markdown syntax elements.
46354ba9607SSascha Wildner  * Can also be used for constant strings when neither escaping
46454ba9607SSascha Wildner  * nor delimiter handling is required.
46554ba9607SSascha Wildner  */
46654ba9607SSascha Wildner static void
md_rawword(const char * s)46754ba9607SSascha Wildner md_rawword(const char *s)
46854ba9607SSascha Wildner {
46954ba9607SSascha Wildner 	md_preword();
47054ba9607SSascha Wildner 
47154ba9607SSascha Wildner 	if (*s == '\0')
47254ba9607SSascha Wildner 		return;
47354ba9607SSascha Wildner 
47454ba9607SSascha Wildner 	if (escflags & ESC_FON) {
47554ba9607SSascha Wildner 		escflags &= ~ESC_FON;
47654ba9607SSascha Wildner 		if (*s == '*' && !code_blocks)
47754ba9607SSascha Wildner 			fputs("&zwnj;", stdout);
47854ba9607SSascha Wildner 	}
47954ba9607SSascha Wildner 
48054ba9607SSascha Wildner 	while (*s != '\0') {
48154ba9607SSascha Wildner 		switch(*s) {
48254ba9607SSascha Wildner 		case '*':
48354ba9607SSascha Wildner 			if (s[1] == '\0')
48454ba9607SSascha Wildner 				escflags |= ESC_FON;
48554ba9607SSascha Wildner 			break;
48654ba9607SSascha Wildner 		case '[':
48754ba9607SSascha Wildner 			escflags |= ESC_SQU;
48854ba9607SSascha Wildner 			break;
48954ba9607SSascha Wildner 		case ']':
49054ba9607SSascha Wildner 			escflags |= ESC_HYP;
49154ba9607SSascha Wildner 			escflags &= ~ESC_SQU;
49254ba9607SSascha Wildner 			break;
49354ba9607SSascha Wildner 		default:
49454ba9607SSascha Wildner 			break;
49554ba9607SSascha Wildner 		}
49654ba9607SSascha Wildner 		md_char(*s++);
49754ba9607SSascha Wildner 	}
49854ba9607SSascha Wildner 	if (s[-1] == ' ')
49954ba9607SSascha Wildner 		escflags |= ESC_EOL;
50054ba9607SSascha Wildner 	else
50154ba9607SSascha Wildner 		escflags &= ~ESC_EOL;
50254ba9607SSascha Wildner }
50354ba9607SSascha Wildner 
50454ba9607SSascha Wildner /*
50554ba9607SSascha Wildner  * Print text and mdoc(7) syntax elements.
50654ba9607SSascha Wildner  */
50754ba9607SSascha Wildner static void
md_word(const char * s)50854ba9607SSascha Wildner md_word(const char *s)
50954ba9607SSascha Wildner {
51054ba9607SSascha Wildner 	const char	*seq, *prevfont, *currfont, *nextfont;
51154ba9607SSascha Wildner 	char		 c;
51254ba9607SSascha Wildner 	int		 bs, sz, uc, breakline;
51354ba9607SSascha Wildner 
51454ba9607SSascha Wildner 	/* No spacing before closing delimiters. */
51554ba9607SSascha Wildner 	if (s[0] != '\0' && s[1] == '\0' &&
51654ba9607SSascha Wildner 	    strchr("!),.:;?]", s[0]) != NULL &&
51754ba9607SSascha Wildner 	    (outflags & MD_spc_force) == 0)
51854ba9607SSascha Wildner 		outflags &= ~MD_spc;
51954ba9607SSascha Wildner 
52054ba9607SSascha Wildner 	md_preword();
52154ba9607SSascha Wildner 
52254ba9607SSascha Wildner 	if (*s == '\0')
52354ba9607SSascha Wildner 		return;
52454ba9607SSascha Wildner 
52554ba9607SSascha Wildner 	/* No spacing after opening delimiters. */
52654ba9607SSascha Wildner 	if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
52754ba9607SSascha Wildner 		outflags &= ~MD_spc;
52854ba9607SSascha Wildner 
52954ba9607SSascha Wildner 	breakline = 0;
53054ba9607SSascha Wildner 	prevfont = currfont = "";
53154ba9607SSascha Wildner 	while ((c = *s++) != '\0') {
53254ba9607SSascha Wildner 		bs = 0;
53354ba9607SSascha Wildner 		switch(c) {
53454ba9607SSascha Wildner 		case ASCII_NBRSP:
53554ba9607SSascha Wildner 			if (code_blocks)
53654ba9607SSascha Wildner 				c = ' ';
53754ba9607SSascha Wildner 			else {
53854ba9607SSascha Wildner 				md_named("nbsp");
53954ba9607SSascha Wildner 				c = '\0';
54054ba9607SSascha Wildner 			}
54154ba9607SSascha Wildner 			break;
54254ba9607SSascha Wildner 		case ASCII_HYPH:
54354ba9607SSascha Wildner 			bs = escflags & ESC_BOL && !code_blocks;
54454ba9607SSascha Wildner 			c = '-';
54554ba9607SSascha Wildner 			break;
54654ba9607SSascha Wildner 		case ASCII_BREAK:
54754ba9607SSascha Wildner 			continue;
54854ba9607SSascha Wildner 		case '#':
54954ba9607SSascha Wildner 		case '+':
55054ba9607SSascha Wildner 		case '-':
55154ba9607SSascha Wildner 			bs = escflags & ESC_BOL && !code_blocks;
55254ba9607SSascha Wildner 			break;
55354ba9607SSascha Wildner 		case '(':
55454ba9607SSascha Wildner 			bs = escflags & ESC_HYP && !code_blocks;
55554ba9607SSascha Wildner 			break;
55654ba9607SSascha Wildner 		case ')':
55754ba9607SSascha Wildner 			bs = escflags & ESC_NUM && !code_blocks;
55854ba9607SSascha Wildner 			break;
55954ba9607SSascha Wildner 		case '*':
56054ba9607SSascha Wildner 		case '[':
56154ba9607SSascha Wildner 		case '_':
56254ba9607SSascha Wildner 		case '`':
56354ba9607SSascha Wildner 			bs = !code_blocks;
56454ba9607SSascha Wildner 			break;
56554ba9607SSascha Wildner 		case '.':
56654ba9607SSascha Wildner 			bs = escflags & ESC_NUM && !code_blocks;
56754ba9607SSascha Wildner 			break;
56854ba9607SSascha Wildner 		case '<':
56954ba9607SSascha Wildner 			if (code_blocks == 0) {
57054ba9607SSascha Wildner 				md_named("lt");
57154ba9607SSascha Wildner 				c = '\0';
57254ba9607SSascha Wildner 			}
57354ba9607SSascha Wildner 			break;
57454ba9607SSascha Wildner 		case '=':
57554ba9607SSascha Wildner 			if (escflags & ESC_BOL && !code_blocks) {
57654ba9607SSascha Wildner 				md_named("equals");
57754ba9607SSascha Wildner 				c = '\0';
57854ba9607SSascha Wildner 			}
57954ba9607SSascha Wildner 			break;
58054ba9607SSascha Wildner 		case '>':
58154ba9607SSascha Wildner 			if (code_blocks == 0) {
58254ba9607SSascha Wildner 				md_named("gt");
58354ba9607SSascha Wildner 				c = '\0';
58454ba9607SSascha Wildner 			}
58554ba9607SSascha Wildner 			break;
58654ba9607SSascha Wildner 		case '\\':
58754ba9607SSascha Wildner 			uc = 0;
58854ba9607SSascha Wildner 			nextfont = NULL;
58954ba9607SSascha Wildner 			switch (mandoc_escape(&s, &seq, &sz)) {
59054ba9607SSascha Wildner 			case ESCAPE_UNICODE:
59154ba9607SSascha Wildner 				uc = mchars_num2uc(seq + 1, sz - 1);
59254ba9607SSascha Wildner 				break;
59354ba9607SSascha Wildner 			case ESCAPE_NUMBERED:
59454ba9607SSascha Wildner 				uc = mchars_num2char(seq, sz);
59554ba9607SSascha Wildner 				break;
59654ba9607SSascha Wildner 			case ESCAPE_SPECIAL:
59754ba9607SSascha Wildner 				uc = mchars_spec2cp(seq, sz);
59854ba9607SSascha Wildner 				break;
59954ba9607SSascha Wildner 			case ESCAPE_UNDEF:
60054ba9607SSascha Wildner 				uc = *seq;
60154ba9607SSascha Wildner 				break;
60254ba9607SSascha Wildner 			case ESCAPE_DEVICE:
60354ba9607SSascha Wildner 				md_rawword("markdown");
60454ba9607SSascha Wildner 				continue;
60554ba9607SSascha Wildner 			case ESCAPE_FONTBOLD:
606*99db7d0eSSascha Wildner 			case ESCAPE_FONTCB:
60754ba9607SSascha Wildner 				nextfont = "**";
60854ba9607SSascha Wildner 				break;
60954ba9607SSascha Wildner 			case ESCAPE_FONTITALIC:
610*99db7d0eSSascha Wildner 			case ESCAPE_FONTCI:
61154ba9607SSascha Wildner 				nextfont = "*";
61254ba9607SSascha Wildner 				break;
61354ba9607SSascha Wildner 			case ESCAPE_FONTBI:
61454ba9607SSascha Wildner 				nextfont = "***";
61554ba9607SSascha Wildner 				break;
61654ba9607SSascha Wildner 			case ESCAPE_FONT:
617*99db7d0eSSascha Wildner 			case ESCAPE_FONTCR:
61854ba9607SSascha Wildner 			case ESCAPE_FONTROMAN:
61954ba9607SSascha Wildner 				nextfont = "";
62054ba9607SSascha Wildner 				break;
62154ba9607SSascha Wildner 			case ESCAPE_FONTPREV:
62254ba9607SSascha Wildner 				nextfont = prevfont;
62354ba9607SSascha Wildner 				break;
62454ba9607SSascha Wildner 			case ESCAPE_BREAK:
62554ba9607SSascha Wildner 				breakline = 1;
62654ba9607SSascha Wildner 				break;
62754ba9607SSascha Wildner 			case ESCAPE_NOSPACE:
62854ba9607SSascha Wildner 			case ESCAPE_SKIPCHAR:
62954ba9607SSascha Wildner 			case ESCAPE_OVERSTRIKE:
63054ba9607SSascha Wildner 				/* XXX not implemented */
63154ba9607SSascha Wildner 				/* FALLTHROUGH */
63254ba9607SSascha Wildner 			case ESCAPE_ERROR:
63354ba9607SSascha Wildner 			default:
63454ba9607SSascha Wildner 				break;
63554ba9607SSascha Wildner 			}
63654ba9607SSascha Wildner 			if (nextfont != NULL && !code_blocks) {
63754ba9607SSascha Wildner 				if (*currfont != '\0') {
63854ba9607SSascha Wildner 					outflags &= ~MD_spc;
63954ba9607SSascha Wildner 					md_rawword(currfont);
64054ba9607SSascha Wildner 				}
64154ba9607SSascha Wildner 				prevfont = currfont;
64254ba9607SSascha Wildner 				currfont = nextfont;
64354ba9607SSascha Wildner 				if (*currfont != '\0') {
64454ba9607SSascha Wildner 					outflags &= ~MD_spc;
64554ba9607SSascha Wildner 					md_rawword(currfont);
64654ba9607SSascha Wildner 				}
64754ba9607SSascha Wildner 			}
64854ba9607SSascha Wildner 			if (uc) {
64954ba9607SSascha Wildner 				if ((uc < 0x20 && uc != 0x09) ||
65054ba9607SSascha Wildner 				    (uc > 0x7E && uc < 0xA0))
65154ba9607SSascha Wildner 					uc = 0xFFFD;
65254ba9607SSascha Wildner 				if (code_blocks) {
65354ba9607SSascha Wildner 					seq = mchars_uc2str(uc);
65454ba9607SSascha Wildner 					fputs(seq, stdout);
65554ba9607SSascha Wildner 					outcount += strlen(seq);
65654ba9607SSascha Wildner 				} else {
65754ba9607SSascha Wildner 					printf("&#%d;", uc);
65854ba9607SSascha Wildner 					outcount++;
65954ba9607SSascha Wildner 				}
66054ba9607SSascha Wildner 				escflags &= ~ESC_FON;
66154ba9607SSascha Wildner 			}
66254ba9607SSascha Wildner 			c = '\0';
66354ba9607SSascha Wildner 			break;
66454ba9607SSascha Wildner 		case ']':
66554ba9607SSascha Wildner 			bs = escflags & ESC_SQU && !code_blocks;
66654ba9607SSascha Wildner 			escflags |= ESC_HYP;
66754ba9607SSascha Wildner 			break;
66854ba9607SSascha Wildner 		default:
66954ba9607SSascha Wildner 			break;
67054ba9607SSascha Wildner 		}
67154ba9607SSascha Wildner 		if (bs)
67254ba9607SSascha Wildner 			putchar('\\');
67354ba9607SSascha Wildner 		md_char(c);
67454ba9607SSascha Wildner 		if (breakline &&
67554ba9607SSascha Wildner 		    (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
67654ba9607SSascha Wildner 			printf("  \n");
67754ba9607SSascha Wildner 			breakline = 0;
67854ba9607SSascha Wildner 			while (*s == ' ' || *s == ASCII_NBRSP)
67954ba9607SSascha Wildner 				s++;
68054ba9607SSascha Wildner 		}
68154ba9607SSascha Wildner 	}
68254ba9607SSascha Wildner 	if (*currfont != '\0') {
68354ba9607SSascha Wildner 		outflags &= ~MD_spc;
68454ba9607SSascha Wildner 		md_rawword(currfont);
68554ba9607SSascha Wildner 	} else if (s[-2] == ' ')
68654ba9607SSascha Wildner 		escflags |= ESC_EOL;
68754ba9607SSascha Wildner 	else
68854ba9607SSascha Wildner 		escflags &= ~ESC_EOL;
68954ba9607SSascha Wildner }
69054ba9607SSascha Wildner 
69154ba9607SSascha Wildner /*
69254ba9607SSascha Wildner  * Print a single HTML named character reference.
69354ba9607SSascha Wildner  */
69454ba9607SSascha Wildner static void
md_named(const char * s)69554ba9607SSascha Wildner md_named(const char *s)
69654ba9607SSascha Wildner {
69754ba9607SSascha Wildner 	printf("&%s;", s);
69854ba9607SSascha Wildner 	escflags &= ~(ESC_FON | ESC_EOL);
69954ba9607SSascha Wildner 	outcount++;
70054ba9607SSascha Wildner }
70154ba9607SSascha Wildner 
70254ba9607SSascha Wildner /*
70354ba9607SSascha Wildner  * Print a single raw character and maintain certain escape flags.
70454ba9607SSascha Wildner  */
70554ba9607SSascha Wildner static void
md_char(unsigned char c)70654ba9607SSascha Wildner md_char(unsigned char c)
70754ba9607SSascha Wildner {
70854ba9607SSascha Wildner 	if (c != '\0') {
70954ba9607SSascha Wildner 		putchar(c);
71054ba9607SSascha Wildner 		if (c == '*')
71154ba9607SSascha Wildner 			escflags |= ESC_FON;
71254ba9607SSascha Wildner 		else
71354ba9607SSascha Wildner 			escflags &= ~ESC_FON;
71454ba9607SSascha Wildner 		outcount++;
71554ba9607SSascha Wildner 	}
71654ba9607SSascha Wildner 	if (c != ']')
71754ba9607SSascha Wildner 		escflags &= ~ESC_HYP;
71854ba9607SSascha Wildner 	if (c == ' ' || c == '\t' || c == '>')
71954ba9607SSascha Wildner 		return;
72054ba9607SSascha Wildner 	if (isdigit(c) == 0)
72154ba9607SSascha Wildner 		escflags &= ~ESC_NUM;
72254ba9607SSascha Wildner 	else if (escflags & ESC_BOL)
72354ba9607SSascha Wildner 		escflags |= ESC_NUM;
72454ba9607SSascha Wildner 	escflags &= ~ESC_BOL;
72554ba9607SSascha Wildner }
72654ba9607SSascha Wildner 
72754ba9607SSascha Wildner static int
md_cond_head(struct roff_node * n)72854ba9607SSascha Wildner md_cond_head(struct roff_node *n)
72954ba9607SSascha Wildner {
73054ba9607SSascha Wildner 	return n->type == ROFFT_HEAD;
73154ba9607SSascha Wildner }
73254ba9607SSascha Wildner 
73354ba9607SSascha Wildner static int
md_cond_body(struct roff_node * n)73454ba9607SSascha Wildner md_cond_body(struct roff_node *n)
73554ba9607SSascha Wildner {
73654ba9607SSascha Wildner 	return n->type == ROFFT_BODY;
73754ba9607SSascha Wildner }
73854ba9607SSascha Wildner 
73954ba9607SSascha Wildner static int
md_pre_abort(struct roff_node * n)74054ba9607SSascha Wildner md_pre_abort(struct roff_node *n)
74154ba9607SSascha Wildner {
74254ba9607SSascha Wildner 	abort();
74354ba9607SSascha Wildner }
74454ba9607SSascha Wildner 
74554ba9607SSascha Wildner static int
md_pre_raw(struct roff_node * n)74654ba9607SSascha Wildner md_pre_raw(struct roff_node *n)
74754ba9607SSascha Wildner {
74854ba9607SSascha Wildner 	const char	*prefix;
74954ba9607SSascha Wildner 
75054ba9607SSascha Wildner 	if ((prefix = md_act(n->tok)->prefix) != NULL) {
75154ba9607SSascha Wildner 		md_rawword(prefix);
75254ba9607SSascha Wildner 		outflags &= ~MD_spc;
75354ba9607SSascha Wildner 		if (*prefix == '`')
75454ba9607SSascha Wildner 			code_blocks++;
75554ba9607SSascha Wildner 	}
75654ba9607SSascha Wildner 	return 1;
75754ba9607SSascha Wildner }
75854ba9607SSascha Wildner 
75954ba9607SSascha Wildner static void
md_post_raw(struct roff_node * n)76054ba9607SSascha Wildner md_post_raw(struct roff_node *n)
76154ba9607SSascha Wildner {
76254ba9607SSascha Wildner 	const char	*suffix;
76354ba9607SSascha Wildner 
76454ba9607SSascha Wildner 	if ((suffix = md_act(n->tok)->suffix) != NULL) {
76554ba9607SSascha Wildner 		outflags &= ~(MD_spc | MD_nl);
76654ba9607SSascha Wildner 		md_rawword(suffix);
76754ba9607SSascha Wildner 		if (*suffix == '`')
76854ba9607SSascha Wildner 			code_blocks--;
76954ba9607SSascha Wildner 	}
77054ba9607SSascha Wildner }
77154ba9607SSascha Wildner 
77254ba9607SSascha Wildner static int
md_pre_word(struct roff_node * n)77354ba9607SSascha Wildner md_pre_word(struct roff_node *n)
77454ba9607SSascha Wildner {
77554ba9607SSascha Wildner 	const char	*prefix;
77654ba9607SSascha Wildner 
77754ba9607SSascha Wildner 	if ((prefix = md_act(n->tok)->prefix) != NULL) {
77854ba9607SSascha Wildner 		md_word(prefix);
77954ba9607SSascha Wildner 		outflags &= ~MD_spc;
78054ba9607SSascha Wildner 	}
78154ba9607SSascha Wildner 	return 1;
78254ba9607SSascha Wildner }
78354ba9607SSascha Wildner 
78454ba9607SSascha Wildner static void
md_post_word(struct roff_node * n)78554ba9607SSascha Wildner md_post_word(struct roff_node *n)
78654ba9607SSascha Wildner {
78754ba9607SSascha Wildner 	const char	*suffix;
78854ba9607SSascha Wildner 
78954ba9607SSascha Wildner 	if ((suffix = md_act(n->tok)->suffix) != NULL) {
79054ba9607SSascha Wildner 		outflags &= ~(MD_spc | MD_nl);
79154ba9607SSascha Wildner 		md_word(suffix);
79254ba9607SSascha Wildner 	}
79354ba9607SSascha Wildner }
79454ba9607SSascha Wildner 
79554ba9607SSascha Wildner static void
md_post_pc(struct roff_node * n)79654ba9607SSascha Wildner md_post_pc(struct roff_node *n)
79754ba9607SSascha Wildner {
798*99db7d0eSSascha Wildner 	struct roff_node *nn;
799*99db7d0eSSascha Wildner 
80054ba9607SSascha Wildner 	md_post_raw(n);
80154ba9607SSascha Wildner 	if (n->parent->tok != MDOC_Rs)
80254ba9607SSascha Wildner 		return;
803*99db7d0eSSascha Wildner 
804*99db7d0eSSascha Wildner 	if ((nn = roff_node_next(n)) != NULL) {
80554ba9607SSascha Wildner 		md_word(",");
806*99db7d0eSSascha Wildner 		if (nn->tok == n->tok &&
807*99db7d0eSSascha Wildner 		    (nn = roff_node_prev(n)) != NULL &&
808*99db7d0eSSascha Wildner 		    nn->tok == n->tok)
80954ba9607SSascha Wildner 			md_word("and");
81054ba9607SSascha Wildner 	} else {
81154ba9607SSascha Wildner 		md_word(".");
81254ba9607SSascha Wildner 		outflags |= MD_nl;
81354ba9607SSascha Wildner 	}
81454ba9607SSascha Wildner }
81554ba9607SSascha Wildner 
81654ba9607SSascha Wildner static int
md_pre_skip(struct roff_node * n)81754ba9607SSascha Wildner md_pre_skip(struct roff_node *n)
81854ba9607SSascha Wildner {
81954ba9607SSascha Wildner 	return 0;
82054ba9607SSascha Wildner }
82154ba9607SSascha Wildner 
82254ba9607SSascha Wildner static void
md_pre_syn(struct roff_node * n)82354ba9607SSascha Wildner md_pre_syn(struct roff_node *n)
82454ba9607SSascha Wildner {
825*99db7d0eSSascha Wildner 	struct roff_node *np;
826*99db7d0eSSascha Wildner 
827*99db7d0eSSascha Wildner 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
828*99db7d0eSSascha Wildner 	    (np = roff_node_prev(n)) == NULL)
82954ba9607SSascha Wildner 		return;
83054ba9607SSascha Wildner 
831*99db7d0eSSascha Wildner 	if (np->tok == n->tok &&
83254ba9607SSascha Wildner 	    n->tok != MDOC_Ft &&
83354ba9607SSascha Wildner 	    n->tok != MDOC_Fo &&
83454ba9607SSascha Wildner 	    n->tok != MDOC_Fn) {
83554ba9607SSascha Wildner 		outflags |= MD_br;
83654ba9607SSascha Wildner 		return;
83754ba9607SSascha Wildner 	}
83854ba9607SSascha Wildner 
839*99db7d0eSSascha Wildner 	switch (np->tok) {
84054ba9607SSascha Wildner 	case MDOC_Fd:
84154ba9607SSascha Wildner 	case MDOC_Fn:
84254ba9607SSascha Wildner 	case MDOC_Fo:
84354ba9607SSascha Wildner 	case MDOC_In:
84454ba9607SSascha Wildner 	case MDOC_Vt:
84554ba9607SSascha Wildner 		outflags |= MD_sp;
84654ba9607SSascha Wildner 		break;
84754ba9607SSascha Wildner 	case MDOC_Ft:
84854ba9607SSascha Wildner 		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
84954ba9607SSascha Wildner 			outflags |= MD_sp;
85054ba9607SSascha Wildner 			break;
85154ba9607SSascha Wildner 		}
85254ba9607SSascha Wildner 		/* FALLTHROUGH */
85354ba9607SSascha Wildner 	default:
85454ba9607SSascha Wildner 		outflags |= MD_br;
85554ba9607SSascha Wildner 		break;
85654ba9607SSascha Wildner 	}
85754ba9607SSascha Wildner }
85854ba9607SSascha Wildner 
85954ba9607SSascha Wildner static int
md_pre_An(struct roff_node * n)86054ba9607SSascha Wildner md_pre_An(struct roff_node *n)
86154ba9607SSascha Wildner {
86254ba9607SSascha Wildner 	switch (n->norm->An.auth) {
86354ba9607SSascha Wildner 	case AUTH_split:
86454ba9607SSascha Wildner 		outflags &= ~MD_An_nosplit;
86554ba9607SSascha Wildner 		outflags |= MD_An_split;
86654ba9607SSascha Wildner 		return 0;
86754ba9607SSascha Wildner 	case AUTH_nosplit:
86854ba9607SSascha Wildner 		outflags &= ~MD_An_split;
86954ba9607SSascha Wildner 		outflags |= MD_An_nosplit;
87054ba9607SSascha Wildner 		return 0;
87154ba9607SSascha Wildner 	default:
87254ba9607SSascha Wildner 		if (outflags & MD_An_split)
87354ba9607SSascha Wildner 			outflags |= MD_br;
87454ba9607SSascha Wildner 		else if (n->sec == SEC_AUTHORS &&
87554ba9607SSascha Wildner 		    ! (outflags & MD_An_nosplit))
87654ba9607SSascha Wildner 			outflags |= MD_An_split;
87754ba9607SSascha Wildner 		return 1;
87854ba9607SSascha Wildner 	}
87954ba9607SSascha Wildner }
88054ba9607SSascha Wildner 
88154ba9607SSascha Wildner static int
md_pre_Ap(struct roff_node * n)88254ba9607SSascha Wildner md_pre_Ap(struct roff_node *n)
88354ba9607SSascha Wildner {
88454ba9607SSascha Wildner 	outflags &= ~MD_spc;
88554ba9607SSascha Wildner 	md_word("'");
88654ba9607SSascha Wildner 	outflags &= ~MD_spc;
88754ba9607SSascha Wildner 	return 0;
88854ba9607SSascha Wildner }
88954ba9607SSascha Wildner 
89054ba9607SSascha Wildner static int
md_pre_Bd(struct roff_node * n)89154ba9607SSascha Wildner md_pre_Bd(struct roff_node *n)
89254ba9607SSascha Wildner {
89354ba9607SSascha Wildner 	switch (n->norm->Bd.type) {
89454ba9607SSascha Wildner 	case DISP_unfilled:
89554ba9607SSascha Wildner 	case DISP_literal:
89654ba9607SSascha Wildner 		return md_pre_Dl(n);
89754ba9607SSascha Wildner 	default:
89854ba9607SSascha Wildner 		return md_pre_D1(n);
89954ba9607SSascha Wildner 	}
90054ba9607SSascha Wildner }
90154ba9607SSascha Wildner 
90254ba9607SSascha Wildner static int
md_pre_Bk(struct roff_node * n)90354ba9607SSascha Wildner md_pre_Bk(struct roff_node *n)
90454ba9607SSascha Wildner {
90554ba9607SSascha Wildner 	switch (n->type) {
90654ba9607SSascha Wildner 	case ROFFT_BLOCK:
90754ba9607SSascha Wildner 		return 1;
90854ba9607SSascha Wildner 	case ROFFT_BODY:
90954ba9607SSascha Wildner 		outflags |= MD_Bk;
91054ba9607SSascha Wildner 		return 1;
91154ba9607SSascha Wildner 	default:
91254ba9607SSascha Wildner 		return 0;
91354ba9607SSascha Wildner 	}
91454ba9607SSascha Wildner }
91554ba9607SSascha Wildner 
91654ba9607SSascha Wildner static void
md_post_Bk(struct roff_node * n)91754ba9607SSascha Wildner md_post_Bk(struct roff_node *n)
91854ba9607SSascha Wildner {
91954ba9607SSascha Wildner 	if (n->type == ROFFT_BODY)
92054ba9607SSascha Wildner 		outflags &= ~MD_Bk;
92154ba9607SSascha Wildner }
92254ba9607SSascha Wildner 
92354ba9607SSascha Wildner static int
md_pre_Bl(struct roff_node * n)92454ba9607SSascha Wildner md_pre_Bl(struct roff_node *n)
92554ba9607SSascha Wildner {
92654ba9607SSascha Wildner 	n->norm->Bl.count = 0;
92754ba9607SSascha Wildner 	if (n->norm->Bl.type == LIST_column)
92854ba9607SSascha Wildner 		md_pre_Dl(n);
92954ba9607SSascha Wildner 	outflags |= MD_sp;
93054ba9607SSascha Wildner 	return 1;
93154ba9607SSascha Wildner }
93254ba9607SSascha Wildner 
93354ba9607SSascha Wildner static void
md_post_Bl(struct roff_node * n)93454ba9607SSascha Wildner md_post_Bl(struct roff_node *n)
93554ba9607SSascha Wildner {
93654ba9607SSascha Wildner 	n->norm->Bl.count = 0;
93754ba9607SSascha Wildner 	if (n->norm->Bl.type == LIST_column)
93854ba9607SSascha Wildner 		md_post_D1(n);
93954ba9607SSascha Wildner 	outflags |= MD_sp;
94054ba9607SSascha Wildner }
94154ba9607SSascha Wildner 
94254ba9607SSascha Wildner static int
md_pre_D1(struct roff_node * n)94354ba9607SSascha Wildner md_pre_D1(struct roff_node *n)
94454ba9607SSascha Wildner {
94554ba9607SSascha Wildner 	/*
94654ba9607SSascha Wildner 	 * Markdown blockquote syntax does not work inside code blocks.
94754ba9607SSascha Wildner 	 * The best we can do is fall back to another nested code block.
94854ba9607SSascha Wildner 	 */
94954ba9607SSascha Wildner 	if (code_blocks) {
95054ba9607SSascha Wildner 		md_stack('\t');
95154ba9607SSascha Wildner 		code_blocks++;
95254ba9607SSascha Wildner 	} else {
95354ba9607SSascha Wildner 		md_stack('>');
95454ba9607SSascha Wildner 		quote_blocks++;
95554ba9607SSascha Wildner 	}
95654ba9607SSascha Wildner 	outflags |= MD_sp;
95754ba9607SSascha Wildner 	return 1;
95854ba9607SSascha Wildner }
95954ba9607SSascha Wildner 
96054ba9607SSascha Wildner static void
md_post_D1(struct roff_node * n)96154ba9607SSascha Wildner md_post_D1(struct roff_node *n)
96254ba9607SSascha Wildner {
96354ba9607SSascha Wildner 	md_stack((char)-1);
96454ba9607SSascha Wildner 	if (code_blocks)
96554ba9607SSascha Wildner 		code_blocks--;
96654ba9607SSascha Wildner 	else
96754ba9607SSascha Wildner 		quote_blocks--;
96854ba9607SSascha Wildner 	outflags |= MD_sp;
96954ba9607SSascha Wildner }
97054ba9607SSascha Wildner 
97154ba9607SSascha Wildner static int
md_pre_Dl(struct roff_node * n)97254ba9607SSascha Wildner md_pre_Dl(struct roff_node *n)
97354ba9607SSascha Wildner {
97454ba9607SSascha Wildner 	/*
97554ba9607SSascha Wildner 	 * Markdown code block syntax does not work inside blockquotes.
97654ba9607SSascha Wildner 	 * The best we can do is fall back to another nested blockquote.
97754ba9607SSascha Wildner 	 */
97854ba9607SSascha Wildner 	if (quote_blocks) {
97954ba9607SSascha Wildner 		md_stack('>');
98054ba9607SSascha Wildner 		quote_blocks++;
98154ba9607SSascha Wildner 	} else {
98254ba9607SSascha Wildner 		md_stack('\t');
98354ba9607SSascha Wildner 		code_blocks++;
98454ba9607SSascha Wildner 	}
98554ba9607SSascha Wildner 	outflags |= MD_sp;
98654ba9607SSascha Wildner 	return 1;
98754ba9607SSascha Wildner }
98854ba9607SSascha Wildner 
98954ba9607SSascha Wildner static int
md_pre_En(struct roff_node * n)99054ba9607SSascha Wildner md_pre_En(struct roff_node *n)
99154ba9607SSascha Wildner {
99254ba9607SSascha Wildner 	if (n->norm->Es == NULL ||
99354ba9607SSascha Wildner 	    n->norm->Es->child == NULL)
99454ba9607SSascha Wildner 		return 1;
99554ba9607SSascha Wildner 
99654ba9607SSascha Wildner 	md_word(n->norm->Es->child->string);
99754ba9607SSascha Wildner 	outflags &= ~MD_spc;
99854ba9607SSascha Wildner 	return 1;
99954ba9607SSascha Wildner }
100054ba9607SSascha Wildner 
100154ba9607SSascha Wildner static void
md_post_En(struct roff_node * n)100254ba9607SSascha Wildner md_post_En(struct roff_node *n)
100354ba9607SSascha Wildner {
100454ba9607SSascha Wildner 	if (n->norm->Es == NULL ||
100554ba9607SSascha Wildner 	    n->norm->Es->child == NULL ||
100654ba9607SSascha Wildner 	    n->norm->Es->child->next == NULL)
100754ba9607SSascha Wildner 		return;
100854ba9607SSascha Wildner 
100954ba9607SSascha Wildner 	outflags &= ~MD_spc;
101054ba9607SSascha Wildner 	md_word(n->norm->Es->child->next->string);
101154ba9607SSascha Wildner }
101254ba9607SSascha Wildner 
101354ba9607SSascha Wildner static int
md_pre_Eo(struct roff_node * n)101454ba9607SSascha Wildner md_pre_Eo(struct roff_node *n)
101554ba9607SSascha Wildner {
101654ba9607SSascha Wildner 	if (n->end == ENDBODY_NOT &&
101754ba9607SSascha Wildner 	    n->parent->head->child == NULL &&
101854ba9607SSascha Wildner 	    n->child != NULL &&
101954ba9607SSascha Wildner 	    n->child->end != ENDBODY_NOT)
102054ba9607SSascha Wildner 		md_preword();
102154ba9607SSascha Wildner 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
102254ba9607SSascha Wildner 	    n->parent->head->child != NULL && (n->child != NULL ||
102354ba9607SSascha Wildner 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
102454ba9607SSascha Wildner 		outflags &= ~(MD_spc | MD_nl);
102554ba9607SSascha Wildner 	return 1;
102654ba9607SSascha Wildner }
102754ba9607SSascha Wildner 
102854ba9607SSascha Wildner static void
md_post_Eo(struct roff_node * n)102954ba9607SSascha Wildner md_post_Eo(struct roff_node *n)
103054ba9607SSascha Wildner {
103154ba9607SSascha Wildner 	if (n->end != ENDBODY_NOT) {
103254ba9607SSascha Wildner 		outflags |= MD_spc;
103354ba9607SSascha Wildner 		return;
103454ba9607SSascha Wildner 	}
103554ba9607SSascha Wildner 
103654ba9607SSascha Wildner 	if (n->child == NULL && n->parent->head->child == NULL)
103754ba9607SSascha Wildner 		return;
103854ba9607SSascha Wildner 
103954ba9607SSascha Wildner 	if (n->parent->tail != NULL && n->parent->tail->child != NULL)
104054ba9607SSascha Wildner 		outflags &= ~MD_spc;
104154ba9607SSascha Wildner         else
104254ba9607SSascha Wildner 		outflags |= MD_spc;
104354ba9607SSascha Wildner }
104454ba9607SSascha Wildner 
104554ba9607SSascha Wildner static int
md_pre_Fa(struct roff_node * n)104654ba9607SSascha Wildner md_pre_Fa(struct roff_node *n)
104754ba9607SSascha Wildner {
104854ba9607SSascha Wildner 	int	 am_Fa;
104954ba9607SSascha Wildner 
105054ba9607SSascha Wildner 	am_Fa = n->tok == MDOC_Fa;
105154ba9607SSascha Wildner 
105254ba9607SSascha Wildner 	if (am_Fa)
105354ba9607SSascha Wildner 		n = n->child;
105454ba9607SSascha Wildner 
105554ba9607SSascha Wildner 	while (n != NULL) {
105654ba9607SSascha Wildner 		md_rawword("*");
105754ba9607SSascha Wildner 		outflags &= ~MD_spc;
105854ba9607SSascha Wildner 		md_node(n);
105954ba9607SSascha Wildner 		outflags &= ~MD_spc;
106054ba9607SSascha Wildner 		md_rawword("*");
106154ba9607SSascha Wildner 		if ((n = n->next) != NULL)
106254ba9607SSascha Wildner 			md_word(",");
106354ba9607SSascha Wildner 	}
106454ba9607SSascha Wildner 	return 0;
106554ba9607SSascha Wildner }
106654ba9607SSascha Wildner 
106754ba9607SSascha Wildner static void
md_post_Fa(struct roff_node * n)106854ba9607SSascha Wildner md_post_Fa(struct roff_node *n)
106954ba9607SSascha Wildner {
1070*99db7d0eSSascha Wildner 	struct roff_node *nn;
1071*99db7d0eSSascha Wildner 
1072*99db7d0eSSascha Wildner 	if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa)
107354ba9607SSascha Wildner 		md_word(",");
107454ba9607SSascha Wildner }
107554ba9607SSascha Wildner 
107654ba9607SSascha Wildner static int
md_pre_Fd(struct roff_node * n)107754ba9607SSascha Wildner md_pre_Fd(struct roff_node *n)
107854ba9607SSascha Wildner {
107954ba9607SSascha Wildner 	md_pre_syn(n);
108054ba9607SSascha Wildner 	md_pre_raw(n);
108154ba9607SSascha Wildner 	return 1;
108254ba9607SSascha Wildner }
108354ba9607SSascha Wildner 
108454ba9607SSascha Wildner static void
md_post_Fd(struct roff_node * n)108554ba9607SSascha Wildner md_post_Fd(struct roff_node *n)
108654ba9607SSascha Wildner {
108754ba9607SSascha Wildner 	md_post_raw(n);
108854ba9607SSascha Wildner 	outflags |= MD_br;
108954ba9607SSascha Wildner }
109054ba9607SSascha Wildner 
109154ba9607SSascha Wildner static void
md_post_Fl(struct roff_node * n)109254ba9607SSascha Wildner md_post_Fl(struct roff_node *n)
109354ba9607SSascha Wildner {
1094*99db7d0eSSascha Wildner 	struct roff_node *nn;
1095*99db7d0eSSascha Wildner 
109654ba9607SSascha Wildner 	md_post_raw(n);
1097*99db7d0eSSascha Wildner 	if (n->child == NULL && (nn = roff_node_next(n)) != NULL &&
1098*99db7d0eSSascha Wildner 	    nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0)
109954ba9607SSascha Wildner 		outflags &= ~MD_spc;
110054ba9607SSascha Wildner }
110154ba9607SSascha Wildner 
110254ba9607SSascha Wildner static int
md_pre_Fn(struct roff_node * n)110354ba9607SSascha Wildner md_pre_Fn(struct roff_node *n)
110454ba9607SSascha Wildner {
110554ba9607SSascha Wildner 	md_pre_syn(n);
110654ba9607SSascha Wildner 
110754ba9607SSascha Wildner 	if ((n = n->child) == NULL)
110854ba9607SSascha Wildner 		return 0;
110954ba9607SSascha Wildner 
111054ba9607SSascha Wildner 	md_rawword("**");
111154ba9607SSascha Wildner 	outflags &= ~MD_spc;
111254ba9607SSascha Wildner 	md_node(n);
111354ba9607SSascha Wildner 	outflags &= ~MD_spc;
111454ba9607SSascha Wildner 	md_rawword("**");
111554ba9607SSascha Wildner 	outflags &= ~MD_spc;
111654ba9607SSascha Wildner 	md_word("(");
111754ba9607SSascha Wildner 
111854ba9607SSascha Wildner 	if ((n = n->next) != NULL)
111954ba9607SSascha Wildner 		md_pre_Fa(n);
112054ba9607SSascha Wildner 	return 0;
112154ba9607SSascha Wildner }
112254ba9607SSascha Wildner 
112354ba9607SSascha Wildner static void
md_post_Fn(struct roff_node * n)112454ba9607SSascha Wildner md_post_Fn(struct roff_node *n)
112554ba9607SSascha Wildner {
112654ba9607SSascha Wildner 	md_word(")");
112754ba9607SSascha Wildner 	if (n->flags & NODE_SYNPRETTY) {
112854ba9607SSascha Wildner 		md_word(";");
112954ba9607SSascha Wildner 		outflags |= MD_sp;
113054ba9607SSascha Wildner 	}
113154ba9607SSascha Wildner }
113254ba9607SSascha Wildner 
113354ba9607SSascha Wildner static int
md_pre_Fo(struct roff_node * n)113454ba9607SSascha Wildner md_pre_Fo(struct roff_node *n)
113554ba9607SSascha Wildner {
113654ba9607SSascha Wildner 	switch (n->type) {
113754ba9607SSascha Wildner 	case ROFFT_BLOCK:
113854ba9607SSascha Wildner 		md_pre_syn(n);
113954ba9607SSascha Wildner 		break;
114054ba9607SSascha Wildner 	case ROFFT_HEAD:
114154ba9607SSascha Wildner 		if (n->child == NULL)
114254ba9607SSascha Wildner 			return 0;
114354ba9607SSascha Wildner 		md_pre_raw(n);
114454ba9607SSascha Wildner 		break;
114554ba9607SSascha Wildner 	case ROFFT_BODY:
114654ba9607SSascha Wildner 		outflags &= ~(MD_spc | MD_nl);
114754ba9607SSascha Wildner 		md_word("(");
114854ba9607SSascha Wildner 		break;
114954ba9607SSascha Wildner 	default:
115054ba9607SSascha Wildner 		break;
115154ba9607SSascha Wildner 	}
115254ba9607SSascha Wildner 	return 1;
115354ba9607SSascha Wildner }
115454ba9607SSascha Wildner 
115554ba9607SSascha Wildner static void
md_post_Fo(struct roff_node * n)115654ba9607SSascha Wildner md_post_Fo(struct roff_node *n)
115754ba9607SSascha Wildner {
115854ba9607SSascha Wildner 	switch (n->type) {
115954ba9607SSascha Wildner 	case ROFFT_HEAD:
116054ba9607SSascha Wildner 		if (n->child != NULL)
116154ba9607SSascha Wildner 			md_post_raw(n);
116254ba9607SSascha Wildner 		break;
116354ba9607SSascha Wildner 	case ROFFT_BODY:
116454ba9607SSascha Wildner 		md_post_Fn(n);
116554ba9607SSascha Wildner 		break;
116654ba9607SSascha Wildner 	default:
116754ba9607SSascha Wildner 		break;
116854ba9607SSascha Wildner 	}
116954ba9607SSascha Wildner }
117054ba9607SSascha Wildner 
117154ba9607SSascha Wildner static int
md_pre_In(struct roff_node * n)117254ba9607SSascha Wildner md_pre_In(struct roff_node *n)
117354ba9607SSascha Wildner {
117454ba9607SSascha Wildner 	if (n->flags & NODE_SYNPRETTY) {
117554ba9607SSascha Wildner 		md_pre_syn(n);
117654ba9607SSascha Wildner 		md_rawword("**");
117754ba9607SSascha Wildner 		outflags &= ~MD_spc;
117854ba9607SSascha Wildner 		md_word("#include <");
117954ba9607SSascha Wildner 	} else {
118054ba9607SSascha Wildner 		md_word("<");
118154ba9607SSascha Wildner 		outflags &= ~MD_spc;
118254ba9607SSascha Wildner 		md_rawword("*");
118354ba9607SSascha Wildner 	}
118454ba9607SSascha Wildner 	outflags &= ~MD_spc;
118554ba9607SSascha Wildner 	return 1;
118654ba9607SSascha Wildner }
118754ba9607SSascha Wildner 
118854ba9607SSascha Wildner static void
md_post_In(struct roff_node * n)118954ba9607SSascha Wildner md_post_In(struct roff_node *n)
119054ba9607SSascha Wildner {
119154ba9607SSascha Wildner 	if (n->flags & NODE_SYNPRETTY) {
119254ba9607SSascha Wildner 		outflags &= ~MD_spc;
119354ba9607SSascha Wildner 		md_rawword(">**");
119454ba9607SSascha Wildner 		outflags |= MD_nl;
119554ba9607SSascha Wildner 	} else {
119654ba9607SSascha Wildner 		outflags &= ~MD_spc;
119754ba9607SSascha Wildner 		md_rawword("*>");
119854ba9607SSascha Wildner 	}
119954ba9607SSascha Wildner }
120054ba9607SSascha Wildner 
120154ba9607SSascha Wildner static int
md_pre_It(struct roff_node * n)120254ba9607SSascha Wildner md_pre_It(struct roff_node *n)
120354ba9607SSascha Wildner {
120454ba9607SSascha Wildner 	struct roff_node	*bln;
120554ba9607SSascha Wildner 
120654ba9607SSascha Wildner 	switch (n->type) {
120754ba9607SSascha Wildner 	case ROFFT_BLOCK:
120854ba9607SSascha Wildner 		return 1;
120954ba9607SSascha Wildner 
121054ba9607SSascha Wildner 	case ROFFT_HEAD:
121154ba9607SSascha Wildner 		bln = n->parent->parent;
121254ba9607SSascha Wildner 		if (bln->norm->Bl.comp == 0 &&
121354ba9607SSascha Wildner 		    bln->norm->Bl.type != LIST_column)
121454ba9607SSascha Wildner 			outflags |= MD_sp;
121554ba9607SSascha Wildner 		outflags |= MD_nl;
121654ba9607SSascha Wildner 
121754ba9607SSascha Wildner 		switch (bln->norm->Bl.type) {
121854ba9607SSascha Wildner 		case LIST_item:
121954ba9607SSascha Wildner 			outflags |= MD_br;
122054ba9607SSascha Wildner 			return 0;
122154ba9607SSascha Wildner 		case LIST_inset:
122254ba9607SSascha Wildner 		case LIST_diag:
122354ba9607SSascha Wildner 		case LIST_ohang:
122454ba9607SSascha Wildner 			outflags |= MD_br;
122554ba9607SSascha Wildner 			return 1;
122654ba9607SSascha Wildner 		case LIST_tag:
122754ba9607SSascha Wildner 		case LIST_hang:
122854ba9607SSascha Wildner 			outflags |= MD_sp;
122954ba9607SSascha Wildner 			return 1;
123054ba9607SSascha Wildner 		case LIST_bullet:
123154ba9607SSascha Wildner 			md_rawword("*\t");
123254ba9607SSascha Wildner 			break;
123354ba9607SSascha Wildner 		case LIST_dash:
123454ba9607SSascha Wildner 		case LIST_hyphen:
123554ba9607SSascha Wildner 			md_rawword("-\t");
123654ba9607SSascha Wildner 			break;
123754ba9607SSascha Wildner 		case LIST_enum:
123854ba9607SSascha Wildner 			md_preword();
123954ba9607SSascha Wildner 			if (bln->norm->Bl.count < 99)
124054ba9607SSascha Wildner 				bln->norm->Bl.count++;
124154ba9607SSascha Wildner 			printf("%d.\t", bln->norm->Bl.count);
124254ba9607SSascha Wildner 			escflags &= ~ESC_FON;
124354ba9607SSascha Wildner 			break;
124454ba9607SSascha Wildner 		case LIST_column:
124554ba9607SSascha Wildner 			outflags |= MD_br;
124654ba9607SSascha Wildner 			return 0;
124754ba9607SSascha Wildner 		default:
124854ba9607SSascha Wildner 			return 0;
124954ba9607SSascha Wildner 		}
125054ba9607SSascha Wildner 		outflags &= ~MD_spc;
125154ba9607SSascha Wildner 		outflags |= MD_nonl;
125254ba9607SSascha Wildner 		outcount = 0;
125354ba9607SSascha Wildner 		md_stack('\t');
125454ba9607SSascha Wildner 		if (code_blocks || quote_blocks)
125554ba9607SSascha Wildner 			list_blocks++;
125654ba9607SSascha Wildner 		return 0;
125754ba9607SSascha Wildner 
125854ba9607SSascha Wildner 	case ROFFT_BODY:
125954ba9607SSascha Wildner 		bln = n->parent->parent;
126054ba9607SSascha Wildner 		switch (bln->norm->Bl.type) {
126154ba9607SSascha Wildner 		case LIST_ohang:
126254ba9607SSascha Wildner 			outflags |= MD_br;
126354ba9607SSascha Wildner 			break;
126454ba9607SSascha Wildner 		case LIST_tag:
126554ba9607SSascha Wildner 		case LIST_hang:
126654ba9607SSascha Wildner 			md_pre_D1(n);
126754ba9607SSascha Wildner 			break;
126854ba9607SSascha Wildner 		default:
126954ba9607SSascha Wildner 			break;
127054ba9607SSascha Wildner 		}
127154ba9607SSascha Wildner 		return 1;
127254ba9607SSascha Wildner 
127354ba9607SSascha Wildner 	default:
127454ba9607SSascha Wildner 		return 0;
127554ba9607SSascha Wildner 	}
127654ba9607SSascha Wildner }
127754ba9607SSascha Wildner 
127854ba9607SSascha Wildner static void
md_post_It(struct roff_node * n)127954ba9607SSascha Wildner md_post_It(struct roff_node *n)
128054ba9607SSascha Wildner {
128154ba9607SSascha Wildner 	struct roff_node	*bln;
128254ba9607SSascha Wildner 	int			 i, nc;
128354ba9607SSascha Wildner 
128454ba9607SSascha Wildner 	if (n->type != ROFFT_BODY)
128554ba9607SSascha Wildner 		return;
128654ba9607SSascha Wildner 
128754ba9607SSascha Wildner 	bln = n->parent->parent;
128854ba9607SSascha Wildner 	switch (bln->norm->Bl.type) {
128954ba9607SSascha Wildner 	case LIST_bullet:
129054ba9607SSascha Wildner 	case LIST_dash:
129154ba9607SSascha Wildner 	case LIST_hyphen:
129254ba9607SSascha Wildner 	case LIST_enum:
129354ba9607SSascha Wildner 		md_stack((char)-1);
129454ba9607SSascha Wildner 		if (code_blocks || quote_blocks)
129554ba9607SSascha Wildner 			list_blocks--;
129654ba9607SSascha Wildner 		break;
129754ba9607SSascha Wildner 	case LIST_tag:
129854ba9607SSascha Wildner 	case LIST_hang:
129954ba9607SSascha Wildner 		md_post_D1(n);
130054ba9607SSascha Wildner 		break;
130154ba9607SSascha Wildner 
130254ba9607SSascha Wildner 	case LIST_column:
130354ba9607SSascha Wildner 		if (n->next == NULL)
130454ba9607SSascha Wildner 			break;
130554ba9607SSascha Wildner 
130654ba9607SSascha Wildner 		/* Calculate the array index of the current column. */
130754ba9607SSascha Wildner 
130854ba9607SSascha Wildner 		i = 0;
130954ba9607SSascha Wildner 		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
131054ba9607SSascha Wildner 			i++;
131154ba9607SSascha Wildner 
131254ba9607SSascha Wildner 		/*
131354ba9607SSascha Wildner 		 * If a width was specified for this column,
131454ba9607SSascha Wildner 		 * subtract what printed, and
131554ba9607SSascha Wildner 		 * add the same spacing as in mdoc_term.c.
131654ba9607SSascha Wildner 		 */
131754ba9607SSascha Wildner 
131854ba9607SSascha Wildner 		nc = bln->norm->Bl.ncols;
131954ba9607SSascha Wildner 		i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
132054ba9607SSascha Wildner 		    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
132154ba9607SSascha Wildner 		if (i < 1)
132254ba9607SSascha Wildner 			i = 1;
132354ba9607SSascha Wildner 		while (i-- > 0)
132454ba9607SSascha Wildner 			putchar(' ');
132554ba9607SSascha Wildner 
132654ba9607SSascha Wildner 		outflags &= ~MD_spc;
132754ba9607SSascha Wildner 		escflags &= ~ESC_FON;
132854ba9607SSascha Wildner 		outcount = 0;
132954ba9607SSascha Wildner 		break;
133054ba9607SSascha Wildner 
133154ba9607SSascha Wildner 	default:
133254ba9607SSascha Wildner 		break;
133354ba9607SSascha Wildner 	}
133454ba9607SSascha Wildner }
133554ba9607SSascha Wildner 
133654ba9607SSascha Wildner static void
md_post_Lb(struct roff_node * n)133754ba9607SSascha Wildner md_post_Lb(struct roff_node *n)
133854ba9607SSascha Wildner {
133954ba9607SSascha Wildner 	if (n->sec == SEC_LIBRARY)
134054ba9607SSascha Wildner 		outflags |= MD_br;
134154ba9607SSascha Wildner }
134254ba9607SSascha Wildner 
134354ba9607SSascha Wildner static void
md_uri(const char * s)134454ba9607SSascha Wildner md_uri(const char *s)
134554ba9607SSascha Wildner {
134654ba9607SSascha Wildner 	while (*s != '\0') {
134754ba9607SSascha Wildner 		if (strchr("%()<>", *s) != NULL) {
134854ba9607SSascha Wildner 			printf("%%%2.2hhX", *s);
134954ba9607SSascha Wildner 			outcount += 3;
135054ba9607SSascha Wildner 		} else {
135154ba9607SSascha Wildner 			putchar(*s);
135254ba9607SSascha Wildner 			outcount++;
135354ba9607SSascha Wildner 		}
135454ba9607SSascha Wildner 		s++;
135554ba9607SSascha Wildner 	}
135654ba9607SSascha Wildner }
135754ba9607SSascha Wildner 
135854ba9607SSascha Wildner static int
md_pre_Lk(struct roff_node * n)135954ba9607SSascha Wildner md_pre_Lk(struct roff_node *n)
136054ba9607SSascha Wildner {
136154ba9607SSascha Wildner 	const struct roff_node *link, *descr, *punct;
136254ba9607SSascha Wildner 
136354ba9607SSascha Wildner 	if ((link = n->child) == NULL)
136454ba9607SSascha Wildner 		return 0;
136554ba9607SSascha Wildner 
136654ba9607SSascha Wildner 	/* Find beginning of trailing punctuation. */
136754ba9607SSascha Wildner 	punct = n->last;
136854ba9607SSascha Wildner 	while (punct != link && punct->flags & NODE_DELIMC)
136954ba9607SSascha Wildner 		punct = punct->prev;
137054ba9607SSascha Wildner 	punct = punct->next;
137154ba9607SSascha Wildner 
137254ba9607SSascha Wildner 	/* Link text. */
137354ba9607SSascha Wildner 	descr = link->next;
137454ba9607SSascha Wildner 	if (descr == punct)
137554ba9607SSascha Wildner 		descr = link;  /* no text */
137654ba9607SSascha Wildner 	md_rawword("[");
137754ba9607SSascha Wildner 	outflags &= ~MD_spc;
137854ba9607SSascha Wildner 	do {
137954ba9607SSascha Wildner 		md_word(descr->string);
138054ba9607SSascha Wildner 		descr = descr->next;
138154ba9607SSascha Wildner 	} while (descr != punct);
138254ba9607SSascha Wildner 	outflags &= ~MD_spc;
138354ba9607SSascha Wildner 
138454ba9607SSascha Wildner 	/* Link target. */
138554ba9607SSascha Wildner 	md_rawword("](");
138654ba9607SSascha Wildner 	md_uri(link->string);
138754ba9607SSascha Wildner 	outflags &= ~MD_spc;
138854ba9607SSascha Wildner 	md_rawword(")");
138954ba9607SSascha Wildner 
139054ba9607SSascha Wildner 	/* Trailing punctuation. */
139154ba9607SSascha Wildner 	while (punct != NULL) {
139254ba9607SSascha Wildner 		md_word(punct->string);
139354ba9607SSascha Wildner 		punct = punct->next;
139454ba9607SSascha Wildner 	}
139554ba9607SSascha Wildner 	return 0;
139654ba9607SSascha Wildner }
139754ba9607SSascha Wildner 
139854ba9607SSascha Wildner static int
md_pre_Mt(struct roff_node * n)139954ba9607SSascha Wildner md_pre_Mt(struct roff_node *n)
140054ba9607SSascha Wildner {
140154ba9607SSascha Wildner 	const struct roff_node *nch;
140254ba9607SSascha Wildner 
140354ba9607SSascha Wildner 	md_rawword("[");
140454ba9607SSascha Wildner 	outflags &= ~MD_spc;
140554ba9607SSascha Wildner 	for (nch = n->child; nch != NULL; nch = nch->next)
140654ba9607SSascha Wildner 		md_word(nch->string);
140754ba9607SSascha Wildner 	outflags &= ~MD_spc;
140854ba9607SSascha Wildner 	md_rawword("](mailto:");
140954ba9607SSascha Wildner 	for (nch = n->child; nch != NULL; nch = nch->next) {
141054ba9607SSascha Wildner 		md_uri(nch->string);
141154ba9607SSascha Wildner 		if (nch->next != NULL) {
141254ba9607SSascha Wildner 			putchar(' ');
141354ba9607SSascha Wildner 			outcount++;
141454ba9607SSascha Wildner 		}
141554ba9607SSascha Wildner 	}
141654ba9607SSascha Wildner 	outflags &= ~MD_spc;
141754ba9607SSascha Wildner 	md_rawword(")");
141854ba9607SSascha Wildner 	return 0;
141954ba9607SSascha Wildner }
142054ba9607SSascha Wildner 
142154ba9607SSascha Wildner static int
md_pre_Nd(struct roff_node * n)142254ba9607SSascha Wildner md_pre_Nd(struct roff_node *n)
142354ba9607SSascha Wildner {
142454ba9607SSascha Wildner 	outflags &= ~MD_nl;
142554ba9607SSascha Wildner 	outflags |= MD_spc;
142654ba9607SSascha Wildner 	md_word("-");
142754ba9607SSascha Wildner 	return 1;
142854ba9607SSascha Wildner }
142954ba9607SSascha Wildner 
143054ba9607SSascha Wildner static int
md_pre_Nm(struct roff_node * n)143154ba9607SSascha Wildner md_pre_Nm(struct roff_node *n)
143254ba9607SSascha Wildner {
143354ba9607SSascha Wildner 	switch (n->type) {
143454ba9607SSascha Wildner 	case ROFFT_BLOCK:
143554ba9607SSascha Wildner 		outflags |= MD_Bk;
143654ba9607SSascha Wildner 		md_pre_syn(n);
143754ba9607SSascha Wildner 		break;
143854ba9607SSascha Wildner 	case ROFFT_HEAD:
143954ba9607SSascha Wildner 	case ROFFT_ELEM:
144054ba9607SSascha Wildner 		md_pre_raw(n);
144154ba9607SSascha Wildner 		break;
144254ba9607SSascha Wildner 	default:
144354ba9607SSascha Wildner 		break;
144454ba9607SSascha Wildner 	}
144554ba9607SSascha Wildner 	return 1;
144654ba9607SSascha Wildner }
144754ba9607SSascha Wildner 
144854ba9607SSascha Wildner static void
md_post_Nm(struct roff_node * n)144954ba9607SSascha Wildner md_post_Nm(struct roff_node *n)
145054ba9607SSascha Wildner {
145154ba9607SSascha Wildner 	switch (n->type) {
145254ba9607SSascha Wildner 	case ROFFT_BLOCK:
145354ba9607SSascha Wildner 		outflags &= ~MD_Bk;
145454ba9607SSascha Wildner 		break;
145554ba9607SSascha Wildner 	case ROFFT_HEAD:
145654ba9607SSascha Wildner 	case ROFFT_ELEM:
145754ba9607SSascha Wildner 		md_post_raw(n);
145854ba9607SSascha Wildner 		break;
145954ba9607SSascha Wildner 	default:
146054ba9607SSascha Wildner 		break;
146154ba9607SSascha Wildner 	}
146254ba9607SSascha Wildner }
146354ba9607SSascha Wildner 
146454ba9607SSascha Wildner static int
md_pre_No(struct roff_node * n)146554ba9607SSascha Wildner md_pre_No(struct roff_node *n)
146654ba9607SSascha Wildner {
146754ba9607SSascha Wildner 	outflags |= MD_spc_force;
146854ba9607SSascha Wildner 	return 1;
146954ba9607SSascha Wildner }
147054ba9607SSascha Wildner 
147154ba9607SSascha Wildner static int
md_pre_Ns(struct roff_node * n)147254ba9607SSascha Wildner md_pre_Ns(struct roff_node *n)
147354ba9607SSascha Wildner {
147454ba9607SSascha Wildner 	outflags &= ~MD_spc;
147554ba9607SSascha Wildner 	return 0;
147654ba9607SSascha Wildner }
147754ba9607SSascha Wildner 
147854ba9607SSascha Wildner static void
md_post_Pf(struct roff_node * n)147954ba9607SSascha Wildner md_post_Pf(struct roff_node *n)
148054ba9607SSascha Wildner {
148154ba9607SSascha Wildner 	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
148254ba9607SSascha Wildner 		outflags &= ~MD_spc;
148354ba9607SSascha Wildner }
148454ba9607SSascha Wildner 
148554ba9607SSascha Wildner static int
md_pre_Pp(struct roff_node * n)148654ba9607SSascha Wildner md_pre_Pp(struct roff_node *n)
148754ba9607SSascha Wildner {
148854ba9607SSascha Wildner 	outflags |= MD_sp;
148954ba9607SSascha Wildner 	return 0;
149054ba9607SSascha Wildner }
149154ba9607SSascha Wildner 
149254ba9607SSascha Wildner static int
md_pre_Rs(struct roff_node * n)149354ba9607SSascha Wildner md_pre_Rs(struct roff_node *n)
149454ba9607SSascha Wildner {
149554ba9607SSascha Wildner 	if (n->sec == SEC_SEE_ALSO)
149654ba9607SSascha Wildner 		outflags |= MD_sp;
149754ba9607SSascha Wildner 	return 1;
149854ba9607SSascha Wildner }
149954ba9607SSascha Wildner 
150054ba9607SSascha Wildner static int
md_pre_Sh(struct roff_node * n)150154ba9607SSascha Wildner md_pre_Sh(struct roff_node *n)
150254ba9607SSascha Wildner {
150354ba9607SSascha Wildner 	switch (n->type) {
150454ba9607SSascha Wildner 	case ROFFT_BLOCK:
150554ba9607SSascha Wildner 		if (n->sec == SEC_AUTHORS)
150654ba9607SSascha Wildner 			outflags &= ~(MD_An_split | MD_An_nosplit);
150754ba9607SSascha Wildner 		break;
150854ba9607SSascha Wildner 	case ROFFT_HEAD:
150954ba9607SSascha Wildner 		outflags |= MD_sp;
151054ba9607SSascha Wildner 		md_rawword(n->tok == MDOC_Sh ? "#" : "##");
151154ba9607SSascha Wildner 		break;
151254ba9607SSascha Wildner 	case ROFFT_BODY:
151354ba9607SSascha Wildner 		outflags |= MD_sp;
151454ba9607SSascha Wildner 		break;
151554ba9607SSascha Wildner 	default:
151654ba9607SSascha Wildner 		break;
151754ba9607SSascha Wildner 	}
151854ba9607SSascha Wildner 	return 1;
151954ba9607SSascha Wildner }
152054ba9607SSascha Wildner 
152154ba9607SSascha Wildner static int
md_pre_Sm(struct roff_node * n)152254ba9607SSascha Wildner md_pre_Sm(struct roff_node *n)
152354ba9607SSascha Wildner {
152454ba9607SSascha Wildner 	if (n->child == NULL)
152554ba9607SSascha Wildner 		outflags ^= MD_Sm;
152654ba9607SSascha Wildner 	else if (strcmp("on", n->child->string) == 0)
152754ba9607SSascha Wildner 		outflags |= MD_Sm;
152854ba9607SSascha Wildner 	else
152954ba9607SSascha Wildner 		outflags &= ~MD_Sm;
153054ba9607SSascha Wildner 
153154ba9607SSascha Wildner 	if (outflags & MD_Sm)
153254ba9607SSascha Wildner 		outflags |= MD_spc;
153354ba9607SSascha Wildner 
153454ba9607SSascha Wildner 	return 0;
153554ba9607SSascha Wildner }
153654ba9607SSascha Wildner 
153754ba9607SSascha Wildner static int
md_pre_Vt(struct roff_node * n)153854ba9607SSascha Wildner md_pre_Vt(struct roff_node *n)
153954ba9607SSascha Wildner {
154054ba9607SSascha Wildner 	switch (n->type) {
154154ba9607SSascha Wildner 	case ROFFT_BLOCK:
154254ba9607SSascha Wildner 		md_pre_syn(n);
154354ba9607SSascha Wildner 		return 1;
154454ba9607SSascha Wildner 	case ROFFT_BODY:
154554ba9607SSascha Wildner 	case ROFFT_ELEM:
154654ba9607SSascha Wildner 		md_pre_raw(n);
154754ba9607SSascha Wildner 		return 1;
154854ba9607SSascha Wildner 	default:
154954ba9607SSascha Wildner 		return 0;
155054ba9607SSascha Wildner 	}
155154ba9607SSascha Wildner }
155254ba9607SSascha Wildner 
155354ba9607SSascha Wildner static void
md_post_Vt(struct roff_node * n)155454ba9607SSascha Wildner md_post_Vt(struct roff_node *n)
155554ba9607SSascha Wildner {
155654ba9607SSascha Wildner 	switch (n->type) {
155754ba9607SSascha Wildner 	case ROFFT_BODY:
155854ba9607SSascha Wildner 	case ROFFT_ELEM:
155954ba9607SSascha Wildner 		md_post_raw(n);
156054ba9607SSascha Wildner 		break;
156154ba9607SSascha Wildner 	default:
156254ba9607SSascha Wildner 		break;
156354ba9607SSascha Wildner 	}
156454ba9607SSascha Wildner }
156554ba9607SSascha Wildner 
156654ba9607SSascha Wildner static int
md_pre_Xr(struct roff_node * n)156754ba9607SSascha Wildner md_pre_Xr(struct roff_node *n)
156854ba9607SSascha Wildner {
156954ba9607SSascha Wildner 	n = n->child;
157054ba9607SSascha Wildner 	if (n == NULL)
157154ba9607SSascha Wildner 		return 0;
157254ba9607SSascha Wildner 	md_node(n);
157354ba9607SSascha Wildner 	n = n->next;
157454ba9607SSascha Wildner 	if (n == NULL)
157554ba9607SSascha Wildner 		return 0;
157654ba9607SSascha Wildner 	outflags &= ~MD_spc;
157754ba9607SSascha Wildner 	md_word("(");
157854ba9607SSascha Wildner 	md_node(n);
157954ba9607SSascha Wildner 	md_word(")");
158054ba9607SSascha Wildner 	return 0;
158154ba9607SSascha Wildner }
158254ba9607SSascha Wildner 
158354ba9607SSascha Wildner static int
md_pre__T(struct roff_node * n)158454ba9607SSascha Wildner md_pre__T(struct roff_node *n)
158554ba9607SSascha Wildner {
158654ba9607SSascha Wildner 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
158754ba9607SSascha Wildner 		md_word("\"");
158854ba9607SSascha Wildner 	else
158954ba9607SSascha Wildner 		md_rawword("*");
159054ba9607SSascha Wildner 	outflags &= ~MD_spc;
159154ba9607SSascha Wildner 	return 1;
159254ba9607SSascha Wildner }
159354ba9607SSascha Wildner 
159454ba9607SSascha Wildner static void
md_post__T(struct roff_node * n)159554ba9607SSascha Wildner md_post__T(struct roff_node *n)
159654ba9607SSascha Wildner {
159754ba9607SSascha Wildner 	outflags &= ~MD_spc;
159854ba9607SSascha Wildner 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
159954ba9607SSascha Wildner 		md_word("\"");
160054ba9607SSascha Wildner 	else
160154ba9607SSascha Wildner 		md_rawword("*");
160254ba9607SSascha Wildner 	md_post_pc(n);
160354ba9607SSascha Wildner }
160454ba9607SSascha Wildner 
160554ba9607SSascha Wildner static int
md_pre_br(struct roff_node * n)160654ba9607SSascha Wildner md_pre_br(struct roff_node *n)
160754ba9607SSascha Wildner {
160854ba9607SSascha Wildner 	outflags |= MD_br;
160954ba9607SSascha Wildner 	return 0;
161054ba9607SSascha Wildner }
1611