xref: /illumos-gate/usr/src/cmd/mandoc/mdoc_markdown.c (revision d1855c8182d5cf1cd290336767a7c8e7537c13a2)
1 /*	$Id: mdoc_markdown.c,v 1.24 2018/04/11 17:11:13 schwarze Exp $ */
2 /*
3  * Copyright (c) 2017 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 *const md_acts = __md_acts - MDOC_Dd;
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 void
254 markdown_mdoc(void *arg, const struct roff_man *mdoc)
255 {
256 	outflags = MD_Sm;
257 	md_word(mdoc->meta.title);
258 	if (mdoc->meta.msec != NULL) {
259 		outflags &= ~MD_spc;
260 		md_word("(");
261 		md_word(mdoc->meta.msec);
262 		md_word(")");
263 	}
264 	md_word("-");
265 	md_word(mdoc->meta.vol);
266 	if (mdoc->meta.arch != NULL) {
267 		md_word("(");
268 		md_word(mdoc->meta.arch);
269 		md_word(")");
270 	}
271 	outflags |= MD_sp;
272 
273 	md_nodelist(mdoc->first->child);
274 
275 	outflags |= MD_sp;
276 	md_word(mdoc->meta.os);
277 	md_word("-");
278 	md_word(mdoc->meta.date);
279 	putchar('\n');
280 }
281 
282 static void
283 md_nodelist(struct roff_node *n)
284 {
285 	while (n != NULL) {
286 		md_node(n);
287 		n = n->next;
288 	}
289 }
290 
291 static void
292 md_node(struct roff_node *n)
293 {
294 	const struct md_act	*act;
295 	int			 cond, process_children;
296 
297 	if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
298 		return;
299 
300 	if (outflags & MD_nonl)
301 		outflags &= ~(MD_nl | MD_sp);
302 	else if (outflags & MD_spc && n->flags & NODE_LINE)
303 		outflags |= MD_nl;
304 
305 	act = NULL;
306 	cond = 0;
307 	process_children = 1;
308 	n->flags &= ~NODE_ENDED;
309 
310 	if (n->type == ROFFT_TEXT) {
311 		if (n->flags & NODE_DELIMC)
312 			outflags &= ~(MD_spc | MD_spc_force);
313 		else if (outflags & MD_Sm)
314 			outflags |= MD_spc_force;
315 		md_word(n->string);
316 		if (n->flags & NODE_DELIMO)
317 			outflags &= ~(MD_spc | MD_spc_force);
318 		else if (outflags & MD_Sm)
319 			outflags |= MD_spc;
320 	} else if (n->tok < ROFF_MAX) {
321 		switch (n->tok) {
322 		case ROFF_br:
323 			process_children = md_pre_br(n);
324 			break;
325 		case ROFF_sp:
326 			process_children = md_pre_Pp(n);
327 			break;
328 		default:
329 			process_children = 0;
330 			break;
331 		}
332 	} else {
333 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
334 		act = md_acts + n->tok;
335 		cond = act->cond == NULL || (*act->cond)(n);
336 		if (cond && act->pre != NULL &&
337 		    (n->end == ENDBODY_NOT || n->child != NULL))
338 			process_children = (*act->pre)(n);
339 	}
340 
341 	if (process_children && n->child != NULL)
342 		md_nodelist(n->child);
343 
344 	if (n->flags & NODE_ENDED)
345 		return;
346 
347 	if (cond && act->post != NULL)
348 		(*act->post)(n);
349 
350 	if (n->end != ENDBODY_NOT)
351 		n->body->flags |= NODE_ENDED;
352 }
353 
354 static const char *
355 md_stack(char c)
356 {
357 	static char	*stack;
358 	static size_t	 sz;
359 	static size_t	 cur;
360 
361 	switch (c) {
362 	case '\0':
363 		break;
364 	case (char)-1:
365 		assert(cur);
366 		stack[--cur] = '\0';
367 		break;
368 	default:
369 		if (cur + 1 >= sz) {
370 			sz += 8;
371 			stack = mandoc_realloc(stack, sz);
372 		}
373 		stack[cur] = c;
374 		stack[++cur] = '\0';
375 		break;
376 	}
377 	return stack == NULL ? "" : stack;
378 }
379 
380 /*
381  * Handle vertical and horizontal spacing.
382  */
383 static void
384 md_preword(void)
385 {
386 	const char	*cp;
387 
388 	/*
389 	 * If a list block is nested inside a code block or a blockquote,
390 	 * blank lines for paragraph breaks no longer work; instead,
391 	 * they terminate the list.  Work around this markdown issue
392 	 * by using mere line breaks instead.
393 	 */
394 
395 	if (list_blocks && outflags & MD_sp) {
396 		outflags &= ~MD_sp;
397 		outflags |= MD_br;
398 	}
399 
400 	/*
401 	 * End the old line if requested.
402 	 * Escape whitespace at the end of the markdown line
403 	 * such that it won't look like an output line break.
404 	 */
405 
406 	if (outflags & MD_sp)
407 		putchar('\n');
408 	else if (outflags & MD_br) {
409 		putchar(' ');
410 		putchar(' ');
411 	} else if (outflags & MD_nl && escflags & ESC_EOL)
412 		md_named("zwnj");
413 
414 	/* Start a new line if necessary. */
415 
416 	if (outflags & (MD_nl | MD_br | MD_sp)) {
417 		putchar('\n');
418 		for (cp = md_stack('\0'); *cp != '\0'; cp++) {
419 			putchar(*cp);
420 			if (*cp == '>')
421 				putchar(' ');
422 		}
423 		outflags &= ~(MD_nl | MD_br | MD_sp);
424 		escflags = ESC_BOL;
425 		outcount = 0;
426 
427 	/* Handle horizontal spacing. */
428 
429 	} else if (outflags & MD_spc) {
430 		if (outflags & MD_Bk)
431 			fputs("&nbsp;", stdout);
432 		else
433 			putchar(' ');
434 		escflags &= ~ESC_FON;
435 		outcount++;
436 	}
437 
438 	outflags &= ~(MD_spc_force | MD_nonl);
439 	if (outflags & MD_Sm)
440 		outflags |= MD_spc;
441 	else
442 		outflags &= ~MD_spc;
443 }
444 
445 /*
446  * Print markdown syntax elements.
447  * Can also be used for constant strings when neither escaping
448  * nor delimiter handling is required.
449  */
450 static void
451 md_rawword(const char *s)
452 {
453 	md_preword();
454 
455 	if (*s == '\0')
456 		return;
457 
458 	if (escflags & ESC_FON) {
459 		escflags &= ~ESC_FON;
460 		if (*s == '*' && !code_blocks)
461 			fputs("&zwnj;", stdout);
462 	}
463 
464 	while (*s != '\0') {
465 		switch(*s) {
466 		case '*':
467 			if (s[1] == '\0')
468 				escflags |= ESC_FON;
469 			break;
470 		case '[':
471 			escflags |= ESC_SQU;
472 			break;
473 		case ']':
474 			escflags |= ESC_HYP;
475 			escflags &= ~ESC_SQU;
476 			break;
477 		default:
478 			break;
479 		}
480 		md_char(*s++);
481 	}
482 	if (s[-1] == ' ')
483 		escflags |= ESC_EOL;
484 	else
485 		escflags &= ~ESC_EOL;
486 }
487 
488 /*
489  * Print text and mdoc(7) syntax elements.
490  */
491 static void
492 md_word(const char *s)
493 {
494 	const char	*seq, *prevfont, *currfont, *nextfont;
495 	char		 c;
496 	int		 bs, sz, uc, breakline;
497 
498 	/* No spacing before closing delimiters. */
499 	if (s[0] != '\0' && s[1] == '\0' &&
500 	    strchr("!),.:;?]", s[0]) != NULL &&
501 	    (outflags & MD_spc_force) == 0)
502 		outflags &= ~MD_spc;
503 
504 	md_preword();
505 
506 	if (*s == '\0')
507 		return;
508 
509 	/* No spacing after opening delimiters. */
510 	if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
511 		outflags &= ~MD_spc;
512 
513 	breakline = 0;
514 	prevfont = currfont = "";
515 	while ((c = *s++) != '\0') {
516 		bs = 0;
517 		switch(c) {
518 		case ASCII_NBRSP:
519 			if (code_blocks)
520 				c = ' ';
521 			else {
522 				md_named("nbsp");
523 				c = '\0';
524 			}
525 			break;
526 		case ASCII_HYPH:
527 			bs = escflags & ESC_BOL && !code_blocks;
528 			c = '-';
529 			break;
530 		case ASCII_BREAK:
531 			continue;
532 		case '#':
533 		case '+':
534 		case '-':
535 			bs = escflags & ESC_BOL && !code_blocks;
536 			break;
537 		case '(':
538 			bs = escflags & ESC_HYP && !code_blocks;
539 			break;
540 		case ')':
541 			bs = escflags & ESC_NUM && !code_blocks;
542 			break;
543 		case '*':
544 		case '[':
545 		case '_':
546 		case '`':
547 			bs = !code_blocks;
548 			break;
549 		case '.':
550 			bs = escflags & ESC_NUM && !code_blocks;
551 			break;
552 		case '<':
553 			if (code_blocks == 0) {
554 				md_named("lt");
555 				c = '\0';
556 			}
557 			break;
558 		case '=':
559 			if (escflags & ESC_BOL && !code_blocks) {
560 				md_named("equals");
561 				c = '\0';
562 			}
563 			break;
564 		case '>':
565 			if (code_blocks == 0) {
566 				md_named("gt");
567 				c = '\0';
568 			}
569 			break;
570 		case '\\':
571 			uc = 0;
572 			nextfont = NULL;
573 			switch (mandoc_escape(&s, &seq, &sz)) {
574 			case ESCAPE_UNICODE:
575 				uc = mchars_num2uc(seq + 1, sz - 1);
576 				break;
577 			case ESCAPE_NUMBERED:
578 				uc = mchars_num2char(seq, sz);
579 				break;
580 			case ESCAPE_SPECIAL:
581 				uc = mchars_spec2cp(seq, sz);
582 				break;
583 			case ESCAPE_FONTBOLD:
584 				nextfont = "**";
585 				break;
586 			case ESCAPE_FONTITALIC:
587 				nextfont = "*";
588 				break;
589 			case ESCAPE_FONTBI:
590 				nextfont = "***";
591 				break;
592 			case ESCAPE_FONT:
593 			case ESCAPE_FONTROMAN:
594 				nextfont = "";
595 				break;
596 			case ESCAPE_FONTPREV:
597 				nextfont = prevfont;
598 				break;
599 			case ESCAPE_BREAK:
600 				breakline = 1;
601 				break;
602 			case ESCAPE_NOSPACE:
603 			case ESCAPE_SKIPCHAR:
604 			case ESCAPE_OVERSTRIKE:
605 				/* XXX not implemented */
606 				/* FALLTHROUGH */
607 			case ESCAPE_ERROR:
608 			default:
609 				break;
610 			}
611 			if (nextfont != NULL && !code_blocks) {
612 				if (*currfont != '\0') {
613 					outflags &= ~MD_spc;
614 					md_rawword(currfont);
615 				}
616 				prevfont = currfont;
617 				currfont = nextfont;
618 				if (*currfont != '\0') {
619 					outflags &= ~MD_spc;
620 					md_rawword(currfont);
621 				}
622 			}
623 			if (uc) {
624 				if ((uc < 0x20 && uc != 0x09) ||
625 				    (uc > 0x7E && uc < 0xA0))
626 					uc = 0xFFFD;
627 				if (code_blocks) {
628 					seq = mchars_uc2str(uc);
629 					fputs(seq, stdout);
630 					outcount += strlen(seq);
631 				} else {
632 					printf("&#%d;", uc);
633 					outcount++;
634 				}
635 				escflags &= ~ESC_FON;
636 			}
637 			c = '\0';
638 			break;
639 		case ']':
640 			bs = escflags & ESC_SQU && !code_blocks;
641 			escflags |= ESC_HYP;
642 			break;
643 		default:
644 			break;
645 		}
646 		if (bs)
647 			putchar('\\');
648 		md_char(c);
649 		if (breakline &&
650 		    (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
651 			printf("  \n");
652 			breakline = 0;
653 			while (*s == ' ' || *s == ASCII_NBRSP)
654 				s++;
655 		}
656 	}
657 	if (*currfont != '\0') {
658 		outflags &= ~MD_spc;
659 		md_rawword(currfont);
660 	} else if (s[-2] == ' ')
661 		escflags |= ESC_EOL;
662 	else
663 		escflags &= ~ESC_EOL;
664 }
665 
666 /*
667  * Print a single HTML named character reference.
668  */
669 static void
670 md_named(const char *s)
671 {
672 	printf("&%s;", s);
673 	escflags &= ~(ESC_FON | ESC_EOL);
674 	outcount++;
675 }
676 
677 /*
678  * Print a single raw character and maintain certain escape flags.
679  */
680 static void
681 md_char(unsigned char c)
682 {
683 	if (c != '\0') {
684 		putchar(c);
685 		if (c == '*')
686 			escflags |= ESC_FON;
687 		else
688 			escflags &= ~ESC_FON;
689 		outcount++;
690 	}
691 	if (c != ']')
692 		escflags &= ~ESC_HYP;
693 	if (c == ' ' || c == '\t' || c == '>')
694 		return;
695 	if (isdigit(c) == 0)
696 		escflags &= ~ESC_NUM;
697 	else if (escflags & ESC_BOL)
698 		escflags |= ESC_NUM;
699 	escflags &= ~ESC_BOL;
700 }
701 
702 static int
703 md_cond_head(struct roff_node *n)
704 {
705 	return n->type == ROFFT_HEAD;
706 }
707 
708 static int
709 md_cond_body(struct roff_node *n)
710 {
711 	return n->type == ROFFT_BODY;
712 }
713 
714 static int
715 md_pre_raw(struct roff_node *n)
716 {
717 	const char	*prefix;
718 
719 	if ((prefix = md_acts[n->tok].prefix) != NULL) {
720 		md_rawword(prefix);
721 		outflags &= ~MD_spc;
722 		if (*prefix == '`')
723 			code_blocks++;
724 	}
725 	return 1;
726 }
727 
728 static void
729 md_post_raw(struct roff_node *n)
730 {
731 	const char	*suffix;
732 
733 	if ((suffix = md_acts[n->tok].suffix) != NULL) {
734 		outflags &= ~(MD_spc | MD_nl);
735 		md_rawword(suffix);
736 		if (*suffix == '`')
737 			code_blocks--;
738 	}
739 }
740 
741 static int
742 md_pre_word(struct roff_node *n)
743 {
744 	const char	*prefix;
745 
746 	if ((prefix = md_acts[n->tok].prefix) != NULL) {
747 		md_word(prefix);
748 		outflags &= ~MD_spc;
749 	}
750 	return 1;
751 }
752 
753 static void
754 md_post_word(struct roff_node *n)
755 {
756 	const char	*suffix;
757 
758 	if ((suffix = md_acts[n->tok].suffix) != NULL) {
759 		outflags &= ~(MD_spc | MD_nl);
760 		md_word(suffix);
761 	}
762 }
763 
764 static void
765 md_post_pc(struct roff_node *n)
766 {
767 	md_post_raw(n);
768 	if (n->parent->tok != MDOC_Rs)
769 		return;
770 	if (n->next != NULL) {
771 		md_word(",");
772 		if (n->prev != NULL &&
773 		    n->prev->tok == n->tok &&
774 		    n->next->tok == n->tok)
775 			md_word("and");
776 	} else {
777 		md_word(".");
778 		outflags |= MD_nl;
779 	}
780 }
781 
782 static int
783 md_pre_skip(struct roff_node *n)
784 {
785 	return 0;
786 }
787 
788 static void
789 md_pre_syn(struct roff_node *n)
790 {
791 	if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
792 		return;
793 
794 	if (n->prev->tok == n->tok &&
795 	    n->tok != MDOC_Ft &&
796 	    n->tok != MDOC_Fo &&
797 	    n->tok != MDOC_Fn) {
798 		outflags |= MD_br;
799 		return;
800 	}
801 
802 	switch (n->prev->tok) {
803 	case MDOC_Fd:
804 	case MDOC_Fn:
805 	case MDOC_Fo:
806 	case MDOC_In:
807 	case MDOC_Vt:
808 		outflags |= MD_sp;
809 		break;
810 	case MDOC_Ft:
811 		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
812 			outflags |= MD_sp;
813 			break;
814 		}
815 		/* FALLTHROUGH */
816 	default:
817 		outflags |= MD_br;
818 		break;
819 	}
820 }
821 
822 static int
823 md_pre_An(struct roff_node *n)
824 {
825 	switch (n->norm->An.auth) {
826 	case AUTH_split:
827 		outflags &= ~MD_An_nosplit;
828 		outflags |= MD_An_split;
829 		return 0;
830 	case AUTH_nosplit:
831 		outflags &= ~MD_An_split;
832 		outflags |= MD_An_nosplit;
833 		return 0;
834 	default:
835 		if (outflags & MD_An_split)
836 			outflags |= MD_br;
837 		else if (n->sec == SEC_AUTHORS &&
838 		    ! (outflags & MD_An_nosplit))
839 			outflags |= MD_An_split;
840 		return 1;
841 	}
842 }
843 
844 static int
845 md_pre_Ap(struct roff_node *n)
846 {
847 	outflags &= ~MD_spc;
848 	md_word("'");
849 	outflags &= ~MD_spc;
850 	return 0;
851 }
852 
853 static int
854 md_pre_Bd(struct roff_node *n)
855 {
856 	switch (n->norm->Bd.type) {
857 	case DISP_unfilled:
858 	case DISP_literal:
859 		return md_pre_Dl(n);
860 	default:
861 		return md_pre_D1(n);
862 	}
863 }
864 
865 static int
866 md_pre_Bk(struct roff_node *n)
867 {
868 	switch (n->type) {
869 	case ROFFT_BLOCK:
870 		return 1;
871 	case ROFFT_BODY:
872 		outflags |= MD_Bk;
873 		return 1;
874 	default:
875 		return 0;
876 	}
877 }
878 
879 static void
880 md_post_Bk(struct roff_node *n)
881 {
882 	if (n->type == ROFFT_BODY)
883 		outflags &= ~MD_Bk;
884 }
885 
886 static int
887 md_pre_Bl(struct roff_node *n)
888 {
889 	n->norm->Bl.count = 0;
890 	if (n->norm->Bl.type == LIST_column)
891 		md_pre_Dl(n);
892 	outflags |= MD_sp;
893 	return 1;
894 }
895 
896 static void
897 md_post_Bl(struct roff_node *n)
898 {
899 	n->norm->Bl.count = 0;
900 	if (n->norm->Bl.type == LIST_column)
901 		md_post_D1(n);
902 	outflags |= MD_sp;
903 }
904 
905 static int
906 md_pre_D1(struct roff_node *n)
907 {
908 	/*
909 	 * Markdown blockquote syntax does not work inside code blocks.
910 	 * The best we can do is fall back to another nested code block.
911 	 */
912 	if (code_blocks) {
913 		md_stack('\t');
914 		code_blocks++;
915 	} else {
916 		md_stack('>');
917 		quote_blocks++;
918 	}
919 	outflags |= MD_sp;
920 	return 1;
921 }
922 
923 static void
924 md_post_D1(struct roff_node *n)
925 {
926 	md_stack((char)-1);
927 	if (code_blocks)
928 		code_blocks--;
929 	else
930 		quote_blocks--;
931 	outflags |= MD_sp;
932 }
933 
934 static int
935 md_pre_Dl(struct roff_node *n)
936 {
937 	/*
938 	 * Markdown code block syntax does not work inside blockquotes.
939 	 * The best we can do is fall back to another nested blockquote.
940 	 */
941 	if (quote_blocks) {
942 		md_stack('>');
943 		quote_blocks++;
944 	} else {
945 		md_stack('\t');
946 		code_blocks++;
947 	}
948 	outflags |= MD_sp;
949 	return 1;
950 }
951 
952 static int
953 md_pre_En(struct roff_node *n)
954 {
955 	if (n->norm->Es == NULL ||
956 	    n->norm->Es->child == NULL)
957 		return 1;
958 
959 	md_word(n->norm->Es->child->string);
960 	outflags &= ~MD_spc;
961 	return 1;
962 }
963 
964 static void
965 md_post_En(struct roff_node *n)
966 {
967 	if (n->norm->Es == NULL ||
968 	    n->norm->Es->child == NULL ||
969 	    n->norm->Es->child->next == NULL)
970 		return;
971 
972 	outflags &= ~MD_spc;
973 	md_word(n->norm->Es->child->next->string);
974 }
975 
976 static int
977 md_pre_Eo(struct roff_node *n)
978 {
979 	if (n->end == ENDBODY_NOT &&
980 	    n->parent->head->child == NULL &&
981 	    n->child != NULL &&
982 	    n->child->end != ENDBODY_NOT)
983 		md_preword();
984 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
985 	    n->parent->head->child != NULL && (n->child != NULL ||
986 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
987 		outflags &= ~(MD_spc | MD_nl);
988 	return 1;
989 }
990 
991 static void
992 md_post_Eo(struct roff_node *n)
993 {
994 	if (n->end != ENDBODY_NOT) {
995 		outflags |= MD_spc;
996 		return;
997 	}
998 
999 	if (n->child == NULL && n->parent->head->child == NULL)
1000 		return;
1001 
1002 	if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1003 		outflags &= ~MD_spc;
1004         else
1005 		outflags |= MD_spc;
1006 }
1007 
1008 static int
1009 md_pre_Fa(struct roff_node *n)
1010 {
1011 	int	 am_Fa;
1012 
1013 	am_Fa = n->tok == MDOC_Fa;
1014 
1015 	if (am_Fa)
1016 		n = n->child;
1017 
1018 	while (n != NULL) {
1019 		md_rawword("*");
1020 		outflags &= ~MD_spc;
1021 		md_node(n);
1022 		outflags &= ~MD_spc;
1023 		md_rawword("*");
1024 		if ((n = n->next) != NULL)
1025 			md_word(",");
1026 	}
1027 	return 0;
1028 }
1029 
1030 static void
1031 md_post_Fa(struct roff_node *n)
1032 {
1033 	if (n->next != NULL && n->next->tok == MDOC_Fa)
1034 		md_word(",");
1035 }
1036 
1037 static int
1038 md_pre_Fd(struct roff_node *n)
1039 {
1040 	md_pre_syn(n);
1041 	md_pre_raw(n);
1042 	return 1;
1043 }
1044 
1045 static void
1046 md_post_Fd(struct roff_node *n)
1047 {
1048 	md_post_raw(n);
1049 	outflags |= MD_br;
1050 }
1051 
1052 static void
1053 md_post_Fl(struct roff_node *n)
1054 {
1055 	md_post_raw(n);
1056 	if (n->child == NULL && n->next != NULL &&
1057 	    n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1058 		outflags &= ~MD_spc;
1059 }
1060 
1061 static int
1062 md_pre_Fn(struct roff_node *n)
1063 {
1064 	md_pre_syn(n);
1065 
1066 	if ((n = n->child) == NULL)
1067 		return 0;
1068 
1069 	md_rawword("**");
1070 	outflags &= ~MD_spc;
1071 	md_node(n);
1072 	outflags &= ~MD_spc;
1073 	md_rawword("**");
1074 	outflags &= ~MD_spc;
1075 	md_word("(");
1076 
1077 	if ((n = n->next) != NULL)
1078 		md_pre_Fa(n);
1079 	return 0;
1080 }
1081 
1082 static void
1083 md_post_Fn(struct roff_node *n)
1084 {
1085 	md_word(")");
1086 	if (n->flags & NODE_SYNPRETTY) {
1087 		md_word(";");
1088 		outflags |= MD_sp;
1089 	}
1090 }
1091 
1092 static int
1093 md_pre_Fo(struct roff_node *n)
1094 {
1095 	switch (n->type) {
1096 	case ROFFT_BLOCK:
1097 		md_pre_syn(n);
1098 		break;
1099 	case ROFFT_HEAD:
1100 		if (n->child == NULL)
1101 			return 0;
1102 		md_pre_raw(n);
1103 		break;
1104 	case ROFFT_BODY:
1105 		outflags &= ~(MD_spc | MD_nl);
1106 		md_word("(");
1107 		break;
1108 	default:
1109 		break;
1110 	}
1111 	return 1;
1112 }
1113 
1114 static void
1115 md_post_Fo(struct roff_node *n)
1116 {
1117 	switch (n->type) {
1118 	case ROFFT_HEAD:
1119 		if (n->child != NULL)
1120 			md_post_raw(n);
1121 		break;
1122 	case ROFFT_BODY:
1123 		md_post_Fn(n);
1124 		break;
1125 	default:
1126 		break;
1127 	}
1128 }
1129 
1130 static int
1131 md_pre_In(struct roff_node *n)
1132 {
1133 	if (n->flags & NODE_SYNPRETTY) {
1134 		md_pre_syn(n);
1135 		md_rawword("**");
1136 		outflags &= ~MD_spc;
1137 		md_word("#include <");
1138 	} else {
1139 		md_word("<");
1140 		outflags &= ~MD_spc;
1141 		md_rawword("*");
1142 	}
1143 	outflags &= ~MD_spc;
1144 	return 1;
1145 }
1146 
1147 static void
1148 md_post_In(struct roff_node *n)
1149 {
1150 	if (n->flags & NODE_SYNPRETTY) {
1151 		outflags &= ~MD_spc;
1152 		md_rawword(">**");
1153 		outflags |= MD_nl;
1154 	} else {
1155 		outflags &= ~MD_spc;
1156 		md_rawword("*>");
1157 	}
1158 }
1159 
1160 static int
1161 md_pre_It(struct roff_node *n)
1162 {
1163 	struct roff_node	*bln;
1164 
1165 	switch (n->type) {
1166 	case ROFFT_BLOCK:
1167 		return 1;
1168 
1169 	case ROFFT_HEAD:
1170 		bln = n->parent->parent;
1171 		if (bln->norm->Bl.comp == 0 &&
1172 		    bln->norm->Bl.type != LIST_column)
1173 			outflags |= MD_sp;
1174 		outflags |= MD_nl;
1175 
1176 		switch (bln->norm->Bl.type) {
1177 		case LIST_item:
1178 			outflags |= MD_br;
1179 			return 0;
1180 		case LIST_inset:
1181 		case LIST_diag:
1182 		case LIST_ohang:
1183 			outflags |= MD_br;
1184 			return 1;
1185 		case LIST_tag:
1186 		case LIST_hang:
1187 			outflags |= MD_sp;
1188 			return 1;
1189 		case LIST_bullet:
1190 			md_rawword("*\t");
1191 			break;
1192 		case LIST_dash:
1193 		case LIST_hyphen:
1194 			md_rawword("-\t");
1195 			break;
1196 		case LIST_enum:
1197 			md_preword();
1198 			if (bln->norm->Bl.count < 99)
1199 				bln->norm->Bl.count++;
1200 			printf("%d.\t", bln->norm->Bl.count);
1201 			escflags &= ~ESC_FON;
1202 			break;
1203 		case LIST_column:
1204 			outflags |= MD_br;
1205 			return 0;
1206 		default:
1207 			return 0;
1208 		}
1209 		outflags &= ~MD_spc;
1210 		outflags |= MD_nonl;
1211 		outcount = 0;
1212 		md_stack('\t');
1213 		if (code_blocks || quote_blocks)
1214 			list_blocks++;
1215 		return 0;
1216 
1217 	case ROFFT_BODY:
1218 		bln = n->parent->parent;
1219 		switch (bln->norm->Bl.type) {
1220 		case LIST_ohang:
1221 			outflags |= MD_br;
1222 			break;
1223 		case LIST_tag:
1224 		case LIST_hang:
1225 			md_pre_D1(n);
1226 			break;
1227 		default:
1228 			break;
1229 		}
1230 		return 1;
1231 
1232 	default:
1233 		return 0;
1234 	}
1235 }
1236 
1237 static void
1238 md_post_It(struct roff_node *n)
1239 {
1240 	struct roff_node	*bln;
1241 	int			 i, nc;
1242 
1243 	if (n->type != ROFFT_BODY)
1244 		return;
1245 
1246 	bln = n->parent->parent;
1247 	switch (bln->norm->Bl.type) {
1248 	case LIST_bullet:
1249 	case LIST_dash:
1250 	case LIST_hyphen:
1251 	case LIST_enum:
1252 		md_stack((char)-1);
1253 		if (code_blocks || quote_blocks)
1254 			list_blocks--;
1255 		break;
1256 	case LIST_tag:
1257 	case LIST_hang:
1258 		md_post_D1(n);
1259 		break;
1260 
1261 	case LIST_column:
1262 		if (n->next == NULL)
1263 			break;
1264 
1265 		/* Calculate the array index of the current column. */
1266 
1267 		i = 0;
1268 		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1269 			i++;
1270 
1271 		/*
1272 		 * If a width was specified for this column,
1273 		 * subtract what printed, and
1274 		 * add the same spacing as in mdoc_term.c.
1275 		 */
1276 
1277 		nc = bln->norm->Bl.ncols;
1278 		i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1279 		    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1280 		if (i < 1)
1281 			i = 1;
1282 		while (i-- > 0)
1283 			putchar(' ');
1284 
1285 		outflags &= ~MD_spc;
1286 		escflags &= ~ESC_FON;
1287 		outcount = 0;
1288 		break;
1289 
1290 	default:
1291 		break;
1292 	}
1293 }
1294 
1295 static void
1296 md_post_Lb(struct roff_node *n)
1297 {
1298 	if (n->sec == SEC_LIBRARY)
1299 		outflags |= MD_br;
1300 }
1301 
1302 static void
1303 md_uri(const char *s)
1304 {
1305 	while (*s != '\0') {
1306 		if (strchr("%()<>", *s) != NULL) {
1307 			printf("%%%2.2hhX", *s);
1308 			outcount += 3;
1309 		} else {
1310 			putchar(*s);
1311 			outcount++;
1312 		}
1313 		s++;
1314 	}
1315 }
1316 
1317 static int
1318 md_pre_Lk(struct roff_node *n)
1319 {
1320 	const struct roff_node *link, *descr, *punct;
1321 
1322 	if ((link = n->child) == NULL)
1323 		return 0;
1324 
1325 	/* Find beginning of trailing punctuation. */
1326 	punct = n->last;
1327 	while (punct != link && punct->flags & NODE_DELIMC)
1328 		punct = punct->prev;
1329 	punct = punct->next;
1330 
1331 	/* Link text. */
1332 	descr = link->next;
1333 	if (descr == punct)
1334 		descr = link;  /* no text */
1335 	md_rawword("[");
1336 	outflags &= ~MD_spc;
1337 	do {
1338 		md_word(descr->string);
1339 		descr = descr->next;
1340 	} while (descr != punct);
1341 	outflags &= ~MD_spc;
1342 
1343 	/* Link target. */
1344 	md_rawword("](");
1345 	md_uri(link->string);
1346 	outflags &= ~MD_spc;
1347 	md_rawword(")");
1348 
1349 	/* Trailing punctuation. */
1350 	while (punct != NULL) {
1351 		md_word(punct->string);
1352 		punct = punct->next;
1353 	}
1354 	return 0;
1355 }
1356 
1357 static int
1358 md_pre_Mt(struct roff_node *n)
1359 {
1360 	const struct roff_node *nch;
1361 
1362 	md_rawword("[");
1363 	outflags &= ~MD_spc;
1364 	for (nch = n->child; nch != NULL; nch = nch->next)
1365 		md_word(nch->string);
1366 	outflags &= ~MD_spc;
1367 	md_rawword("](mailto:");
1368 	for (nch = n->child; nch != NULL; nch = nch->next) {
1369 		md_uri(nch->string);
1370 		if (nch->next != NULL) {
1371 			putchar(' ');
1372 			outcount++;
1373 		}
1374 	}
1375 	outflags &= ~MD_spc;
1376 	md_rawword(")");
1377 	return 0;
1378 }
1379 
1380 static int
1381 md_pre_Nd(struct roff_node *n)
1382 {
1383 	outflags &= ~MD_nl;
1384 	outflags |= MD_spc;
1385 	md_word("-");
1386 	return 1;
1387 }
1388 
1389 static int
1390 md_pre_Nm(struct roff_node *n)
1391 {
1392 	switch (n->type) {
1393 	case ROFFT_BLOCK:
1394 		outflags |= MD_Bk;
1395 		md_pre_syn(n);
1396 		break;
1397 	case ROFFT_HEAD:
1398 	case ROFFT_ELEM:
1399 		md_pre_raw(n);
1400 		break;
1401 	default:
1402 		break;
1403 	}
1404 	return 1;
1405 }
1406 
1407 static void
1408 md_post_Nm(struct roff_node *n)
1409 {
1410 	switch (n->type) {
1411 	case ROFFT_BLOCK:
1412 		outflags &= ~MD_Bk;
1413 		break;
1414 	case ROFFT_HEAD:
1415 	case ROFFT_ELEM:
1416 		md_post_raw(n);
1417 		break;
1418 	default:
1419 		break;
1420 	}
1421 }
1422 
1423 static int
1424 md_pre_No(struct roff_node *n)
1425 {
1426 	outflags |= MD_spc_force;
1427 	return 1;
1428 }
1429 
1430 static int
1431 md_pre_Ns(struct roff_node *n)
1432 {
1433 	outflags &= ~MD_spc;
1434 	return 0;
1435 }
1436 
1437 static void
1438 md_post_Pf(struct roff_node *n)
1439 {
1440 	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1441 		outflags &= ~MD_spc;
1442 }
1443 
1444 static int
1445 md_pre_Pp(struct roff_node *n)
1446 {
1447 	outflags |= MD_sp;
1448 	return 0;
1449 }
1450 
1451 static int
1452 md_pre_Rs(struct roff_node *n)
1453 {
1454 	if (n->sec == SEC_SEE_ALSO)
1455 		outflags |= MD_sp;
1456 	return 1;
1457 }
1458 
1459 static int
1460 md_pre_Sh(struct roff_node *n)
1461 {
1462 	switch (n->type) {
1463 	case ROFFT_BLOCK:
1464 		if (n->sec == SEC_AUTHORS)
1465 			outflags &= ~(MD_An_split | MD_An_nosplit);
1466 		break;
1467 	case ROFFT_HEAD:
1468 		outflags |= MD_sp;
1469 		md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1470 		break;
1471 	case ROFFT_BODY:
1472 		outflags |= MD_sp;
1473 		break;
1474 	default:
1475 		break;
1476 	}
1477 	return 1;
1478 }
1479 
1480 static int
1481 md_pre_Sm(struct roff_node *n)
1482 {
1483 	if (n->child == NULL)
1484 		outflags ^= MD_Sm;
1485 	else if (strcmp("on", n->child->string) == 0)
1486 		outflags |= MD_Sm;
1487 	else
1488 		outflags &= ~MD_Sm;
1489 
1490 	if (outflags & MD_Sm)
1491 		outflags |= MD_spc;
1492 
1493 	return 0;
1494 }
1495 
1496 static int
1497 md_pre_Vt(struct roff_node *n)
1498 {
1499 	switch (n->type) {
1500 	case ROFFT_BLOCK:
1501 		md_pre_syn(n);
1502 		return 1;
1503 	case ROFFT_BODY:
1504 	case ROFFT_ELEM:
1505 		md_pre_raw(n);
1506 		return 1;
1507 	default:
1508 		return 0;
1509 	}
1510 }
1511 
1512 static void
1513 md_post_Vt(struct roff_node *n)
1514 {
1515 	switch (n->type) {
1516 	case ROFFT_BODY:
1517 	case ROFFT_ELEM:
1518 		md_post_raw(n);
1519 		break;
1520 	default:
1521 		break;
1522 	}
1523 }
1524 
1525 static int
1526 md_pre_Xr(struct roff_node *n)
1527 {
1528 	n = n->child;
1529 	if (n == NULL)
1530 		return 0;
1531 	md_node(n);
1532 	n = n->next;
1533 	if (n == NULL)
1534 		return 0;
1535 	outflags &= ~MD_spc;
1536 	md_word("(");
1537 	md_node(n);
1538 	md_word(")");
1539 	return 0;
1540 }
1541 
1542 static int
1543 md_pre__T(struct roff_node *n)
1544 {
1545 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1546 		md_word("\"");
1547 	else
1548 		md_rawword("*");
1549 	outflags &= ~MD_spc;
1550 	return 1;
1551 }
1552 
1553 static void
1554 md_post__T(struct roff_node *n)
1555 {
1556 	outflags &= ~MD_spc;
1557 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1558 		md_word("\"");
1559 	else
1560 		md_rawword("*");
1561 	md_post_pc(n);
1562 }
1563 
1564 static int
1565 md_pre_br(struct roff_node *n)
1566 {
1567 	outflags |= MD_br;
1568 	return 0;
1569 }
1570