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