]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/mdoc_markdown.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r308421, and update
[FreeBSD/FreeBSD.git] / contrib / mdocml / mdoc_markdown.c
1 /*      $Id: mdoc_markdown.c,v 1.22 2017/05/30 16:31:29 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->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;
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         prevfont = currfont = "";
514         while ((c = *s++) != '\0') {
515                 bs = 0;
516                 switch(c) {
517                 case ASCII_NBRSP:
518                         if (code_blocks)
519                                 c = ' ';
520                         else {
521                                 md_named("nbsp");
522                                 c = '\0';
523                         }
524                         break;
525                 case ASCII_HYPH:
526                         bs = escflags & ESC_BOL && !code_blocks;
527                         c = '-';
528                         break;
529                 case ASCII_BREAK:
530                         continue;
531                 case '#':
532                 case '+':
533                 case '-':
534                         bs = escflags & ESC_BOL && !code_blocks;
535                         break;
536                 case '(':
537                         bs = escflags & ESC_HYP && !code_blocks;
538                         break;
539                 case ')':
540                         bs = escflags & ESC_NUM && !code_blocks;
541                         break;
542                 case '*':
543                 case '[':
544                 case '_':
545                 case '`':
546                         bs = !code_blocks;
547                         break;
548                 case '.':
549                         bs = escflags & ESC_NUM && !code_blocks;
550                         break;
551                 case '<':
552                         if (code_blocks == 0) {
553                                 md_named("lt");
554                                 c = '\0';
555                         }
556                         break;
557                 case '=':
558                         if (escflags & ESC_BOL && !code_blocks) {
559                                 md_named("equals");
560                                 c = '\0';
561                         }
562                         break;
563                 case '>':
564                         if (code_blocks == 0) {
565                                 md_named("gt");
566                                 c = '\0';
567                         }
568                         break;
569                 case '\\':
570                         uc = 0;
571                         nextfont = NULL;
572                         switch (mandoc_escape(&s, &seq, &sz)) {
573                         case ESCAPE_UNICODE:
574                                 uc = mchars_num2uc(seq + 1, sz - 1);
575                                 break;
576                         case ESCAPE_NUMBERED:
577                                 uc = mchars_num2char(seq, sz);
578                                 break;
579                         case ESCAPE_SPECIAL:
580                                 uc = mchars_spec2cp(seq, sz);
581                                 break;
582                         case ESCAPE_FONTBOLD:
583                                 nextfont = "**";
584                                 break;
585                         case ESCAPE_FONTITALIC:
586                                 nextfont = "*";
587                                 break;
588                         case ESCAPE_FONTBI:
589                                 nextfont = "***";
590                                 break;
591                         case ESCAPE_FONT:
592                         case ESCAPE_FONTROMAN:
593                                 nextfont = "";
594                                 break;
595                         case ESCAPE_FONTPREV:
596                                 nextfont = prevfont;
597                                 break;
598                         case ESCAPE_NOSPACE:
599                         case ESCAPE_SKIPCHAR:
600                         case ESCAPE_OVERSTRIKE:
601                                 /* XXX not implemented */
602                                 /* FALLTHROUGH */
603                         case ESCAPE_ERROR:
604                         default:
605                                 break;
606                         }
607                         if (nextfont != NULL && !code_blocks) {
608                                 if (*currfont != '\0') {
609                                         outflags &= ~MD_spc;
610                                         md_rawword(currfont);
611                                 }
612                                 prevfont = currfont;
613                                 currfont = nextfont;
614                                 if (*currfont != '\0') {
615                                         outflags &= ~MD_spc;
616                                         md_rawword(currfont);
617                                 }
618                         }
619                         if (uc) {
620                                 if ((uc < 0x20 && uc != 0x09) ||
621                                     (uc > 0x7E && uc < 0xA0))
622                                         uc = 0xFFFD;
623                                 if (code_blocks) {
624                                         seq = mchars_uc2str(uc);
625                                         fputs(seq, stdout);
626                                         outcount += strlen(seq);
627                                 } else {
628                                         printf("&#%d;", uc);
629                                         outcount++;
630                                 }
631                                 escflags &= ~ESC_FON;
632                         }
633                         c = '\0';
634                         break;
635                 case ']':
636                         bs = escflags & ESC_SQU && !code_blocks;
637                         escflags |= ESC_HYP;
638                         break;
639                 default:
640                         break;
641                 }
642                 if (bs)
643                         putchar('\\');
644                 md_char(c);
645         }
646         if (*currfont != '\0') {
647                 outflags &= ~MD_spc;
648                 md_rawword(currfont);
649         } else if (s[-2] == ' ')
650                 escflags |= ESC_EOL;
651         else
652                 escflags &= ~ESC_EOL;
653 }
654
655 /*
656  * Print a single HTML named character reference.
657  */
658 static void
659 md_named(const char *s)
660 {
661         printf("&%s;", s);
662         escflags &= ~(ESC_FON | ESC_EOL);
663         outcount++;
664 }
665
666 /*
667  * Print a single raw character and maintain certain escape flags.
668  */
669 static void
670 md_char(unsigned char c)
671 {
672         if (c != '\0') {
673                 putchar(c);
674                 if (c == '*')
675                         escflags |= ESC_FON;
676                 else
677                         escflags &= ~ESC_FON;
678                 outcount++;
679         }
680         if (c != ']')
681                 escflags &= ~ESC_HYP;
682         if (c == ' ' || c == '\t' || c == '>')
683                 return;
684         if (isdigit(c) == 0)
685                 escflags &= ~ESC_NUM;
686         else if (escflags & ESC_BOL)
687                 escflags |= ESC_NUM;
688         escflags &= ~ESC_BOL;
689 }
690
691 static int
692 md_cond_head(struct roff_node *n)
693 {
694         return n->type == ROFFT_HEAD;
695 }
696
697 static int
698 md_cond_body(struct roff_node *n)
699 {
700         return n->type == ROFFT_BODY;
701 }
702
703 static int
704 md_pre_raw(struct roff_node *n)
705 {
706         const char      *prefix;
707
708         if ((prefix = md_acts[n->tok].prefix) != NULL) {
709                 md_rawword(prefix);
710                 outflags &= ~MD_spc;
711                 if (*prefix == '`')
712                         code_blocks++;
713         }
714         return 1;
715 }
716
717 static void
718 md_post_raw(struct roff_node *n)
719 {
720         const char      *suffix;
721
722         if ((suffix = md_acts[n->tok].suffix) != NULL) {
723                 outflags &= ~(MD_spc | MD_nl);
724                 md_rawword(suffix);
725                 if (*suffix == '`')
726                         code_blocks--;
727         }
728 }
729
730 static int
731 md_pre_word(struct roff_node *n)
732 {
733         const char      *prefix;
734
735         if ((prefix = md_acts[n->tok].prefix) != NULL) {
736                 md_word(prefix);
737                 outflags &= ~MD_spc;
738         }
739         return 1;
740 }
741
742 static void
743 md_post_word(struct roff_node *n)
744 {
745         const char      *suffix;
746
747         if ((suffix = md_acts[n->tok].suffix) != NULL) {
748                 outflags &= ~(MD_spc | MD_nl);
749                 md_word(suffix);
750         }
751 }
752
753 static void
754 md_post_pc(struct roff_node *n)
755 {
756         md_post_raw(n);
757         if (n->parent->tok != MDOC_Rs)
758                 return;
759         if (n->next != NULL) {
760                 md_word(",");
761                 if (n->prev != NULL &&
762                     n->prev->tok == n->tok &&
763                     n->next->tok == n->tok)
764                         md_word("and");
765         } else {
766                 md_word(".");
767                 outflags |= MD_nl;
768         }
769 }
770
771 static int
772 md_pre_skip(struct roff_node *n)
773 {
774         return 0;
775 }
776
777 static void
778 md_pre_syn(struct roff_node *n)
779 {
780         if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
781                 return;
782
783         if (n->prev->tok == n->tok &&
784             n->tok != MDOC_Ft &&
785             n->tok != MDOC_Fo &&
786             n->tok != MDOC_Fn) {
787                 outflags |= MD_br;
788                 return;
789         }
790
791         switch (n->prev->tok) {
792         case MDOC_Fd:
793         case MDOC_Fn:
794         case MDOC_Fo:
795         case MDOC_In:
796         case MDOC_Vt:
797                 outflags |= MD_sp;
798                 break;
799         case MDOC_Ft:
800                 if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
801                         outflags |= MD_sp;
802                         break;
803                 }
804                 /* FALLTHROUGH */
805         default:
806                 outflags |= MD_br;
807                 break;
808         }
809 }
810
811 static int
812 md_pre_An(struct roff_node *n)
813 {
814         switch (n->norm->An.auth) {
815         case AUTH_split:
816                 outflags &= ~MD_An_nosplit;
817                 outflags |= MD_An_split;
818                 return 0;
819         case AUTH_nosplit:
820                 outflags &= ~MD_An_split;
821                 outflags |= MD_An_nosplit;
822                 return 0;
823         default:
824                 if (outflags & MD_An_split)
825                         outflags |= MD_br;
826                 else if (n->sec == SEC_AUTHORS &&
827                     ! (outflags & MD_An_nosplit))
828                         outflags |= MD_An_split;
829                 return 1;
830         }
831 }
832
833 static int
834 md_pre_Ap(struct roff_node *n)
835 {
836         outflags &= ~MD_spc;
837         md_word("'");
838         outflags &= ~MD_spc;
839         return 0;
840 }
841
842 static int
843 md_pre_Bd(struct roff_node *n)
844 {
845         switch (n->norm->Bd.type) {
846         case DISP_unfilled:
847         case DISP_literal:
848                 return md_pre_Dl(n);
849         default:
850                 return md_pre_D1(n);
851         }
852 }
853
854 static int
855 md_pre_Bk(struct roff_node *n)
856 {
857         switch (n->type) {
858         case ROFFT_BLOCK:
859                 return 1;
860         case ROFFT_BODY:
861                 outflags |= MD_Bk;
862                 return 1;
863         default:
864                 return 0;
865         }
866 }
867
868 static void
869 md_post_Bk(struct roff_node *n)
870 {
871         if (n->type == ROFFT_BODY)
872                 outflags &= ~MD_Bk;
873 }
874
875 static int
876 md_pre_Bl(struct roff_node *n)
877 {
878         n->norm->Bl.count = 0;
879         if (n->norm->Bl.type == LIST_column)
880                 md_pre_Dl(n);
881         outflags |= MD_sp;
882         return 1;
883 }
884
885 static void
886 md_post_Bl(struct roff_node *n)
887 {
888         n->norm->Bl.count = 0;
889         if (n->norm->Bl.type == LIST_column)
890                 md_post_D1(n);
891         outflags |= MD_sp;
892 }
893
894 static int
895 md_pre_D1(struct roff_node *n)
896 {
897         /*
898          * Markdown blockquote syntax does not work inside code blocks.
899          * The best we can do is fall back to another nested code block.
900          */
901         if (code_blocks) {
902                 md_stack('\t');
903                 code_blocks++;
904         } else {
905                 md_stack('>');
906                 quote_blocks++;
907         }
908         outflags |= MD_sp;
909         return 1;
910 }
911
912 static void
913 md_post_D1(struct roff_node *n)
914 {
915         md_stack((char)-1);
916         if (code_blocks)
917                 code_blocks--;
918         else
919                 quote_blocks--;
920         outflags |= MD_sp;
921 }
922
923 static int
924 md_pre_Dl(struct roff_node *n)
925 {
926         /*
927          * Markdown code block syntax does not work inside blockquotes.
928          * The best we can do is fall back to another nested blockquote.
929          */
930         if (quote_blocks) {
931                 md_stack('>');
932                 quote_blocks++;
933         } else {
934                 md_stack('\t');
935                 code_blocks++;
936         }
937         outflags |= MD_sp;
938         return 1;
939 }
940
941 static int
942 md_pre_En(struct roff_node *n)
943 {
944         if (n->norm->Es == NULL ||
945             n->norm->Es->child == NULL)
946                 return 1;
947
948         md_word(n->norm->Es->child->string);
949         outflags &= ~MD_spc;
950         return 1;
951 }
952
953 static void
954 md_post_En(struct roff_node *n)
955 {
956         if (n->norm->Es == NULL ||
957             n->norm->Es->child == NULL ||
958             n->norm->Es->child->next == NULL)
959                 return;
960
961         outflags &= ~MD_spc;
962         md_word(n->norm->Es->child->next->string);
963 }
964
965 static int
966 md_pre_Eo(struct roff_node *n)
967 {
968         if (n->end == ENDBODY_NOT &&
969             n->parent->head->child == NULL &&
970             n->child != NULL &&
971             n->child->end != ENDBODY_NOT)
972                 md_preword();
973         else if (n->end != ENDBODY_NOT ? n->child != NULL :
974             n->parent->head->child != NULL && (n->child != NULL ||
975             (n->parent->tail != NULL && n->parent->tail->child != NULL)))
976                 outflags &= ~(MD_spc | MD_nl);
977         return 1;
978 }
979
980 static void
981 md_post_Eo(struct roff_node *n)
982 {
983         if (n->end != ENDBODY_NOT) {
984                 outflags |= MD_spc;
985                 return;
986         }
987
988         if (n->child == NULL && n->parent->head->child == NULL)
989                 return;
990
991         if (n->parent->tail != NULL && n->parent->tail->child != NULL)
992                 outflags &= ~MD_spc;
993         else
994                 outflags |= MD_spc;
995 }
996
997 static int
998 md_pre_Fa(struct roff_node *n)
999 {
1000         int      am_Fa;
1001
1002         am_Fa = n->tok == MDOC_Fa;
1003
1004         if (am_Fa)
1005                 n = n->child;
1006
1007         while (n != NULL) {
1008                 md_rawword("*");
1009                 outflags &= ~MD_spc;
1010                 md_node(n);
1011                 outflags &= ~MD_spc;
1012                 md_rawword("*");
1013                 if ((n = n->next) != NULL)
1014                         md_word(",");
1015         }
1016         return 0;
1017 }
1018
1019 static void
1020 md_post_Fa(struct roff_node *n)
1021 {
1022         if (n->next != NULL && n->next->tok == MDOC_Fa)
1023                 md_word(",");
1024 }
1025
1026 static int
1027 md_pre_Fd(struct roff_node *n)
1028 {
1029         md_pre_syn(n);
1030         md_pre_raw(n);
1031         return 1;
1032 }
1033
1034 static void
1035 md_post_Fd(struct roff_node *n)
1036 {
1037         md_post_raw(n);
1038         outflags |= MD_br;
1039 }
1040
1041 static void
1042 md_post_Fl(struct roff_node *n)
1043 {
1044         md_post_raw(n);
1045         if (n->child == NULL && n->next != NULL &&
1046             n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1047                 outflags &= ~MD_spc;
1048 }
1049
1050 static int
1051 md_pre_Fn(struct roff_node *n)
1052 {
1053         md_pre_syn(n);
1054
1055         if ((n = n->child) == NULL)
1056                 return 0;
1057
1058         md_rawword("**");
1059         outflags &= ~MD_spc;
1060         md_node(n);
1061         outflags &= ~MD_spc;
1062         md_rawword("**");
1063         outflags &= ~MD_spc;
1064         md_word("(");
1065
1066         if ((n = n->next) != NULL)
1067                 md_pre_Fa(n);
1068         return 0;
1069 }
1070
1071 static void
1072 md_post_Fn(struct roff_node *n)
1073 {
1074         md_word(")");
1075         if (n->flags & NODE_SYNPRETTY) {
1076                 md_word(";");
1077                 outflags |= MD_sp;
1078         }
1079 }
1080
1081 static int
1082 md_pre_Fo(struct roff_node *n)
1083 {
1084         switch (n->type) {
1085         case ROFFT_BLOCK:
1086                 md_pre_syn(n);
1087                 break;
1088         case ROFFT_HEAD:
1089                 if (n->child == NULL)
1090                         return 0;
1091                 md_pre_raw(n);
1092                 break;
1093         case ROFFT_BODY:
1094                 outflags &= ~(MD_spc | MD_nl);
1095                 md_word("(");
1096                 break;
1097         default:
1098                 break;
1099         }
1100         return 1;
1101 }
1102
1103 static void
1104 md_post_Fo(struct roff_node *n)
1105 {
1106         switch (n->type) {
1107         case ROFFT_HEAD:
1108                 if (n->child != NULL)
1109                         md_post_raw(n);
1110                 break;
1111         case ROFFT_BODY:
1112                 md_post_Fn(n);
1113                 break;
1114         default:
1115                 break;
1116         }
1117 }
1118
1119 static int
1120 md_pre_In(struct roff_node *n)
1121 {
1122         if (n->flags & NODE_SYNPRETTY) {
1123                 md_pre_syn(n);
1124                 md_rawword("**");
1125                 outflags &= ~MD_spc;
1126                 md_word("#include <");
1127         } else {
1128                 md_word("<");
1129                 outflags &= ~MD_spc;
1130                 md_rawword("*");
1131         }
1132         outflags &= ~MD_spc;
1133         return 1;
1134 }
1135
1136 static void
1137 md_post_In(struct roff_node *n)
1138 {
1139         if (n->flags & NODE_SYNPRETTY) {
1140                 outflags &= ~MD_spc;
1141                 md_rawword(">**");
1142                 outflags |= MD_nl;
1143         } else {
1144                 outflags &= ~MD_spc;
1145                 md_rawword("*>");
1146         }
1147 }
1148
1149 static int
1150 md_pre_It(struct roff_node *n)
1151 {
1152         struct roff_node        *bln;
1153
1154         switch (n->type) {
1155         case ROFFT_BLOCK:
1156                 return 1;
1157
1158         case ROFFT_HEAD:
1159                 bln = n->parent->parent;
1160                 if (bln->norm->Bl.comp == 0 &&
1161                     bln->norm->Bl.type != LIST_column)
1162                         outflags |= MD_sp;
1163                 outflags |= MD_nl;
1164
1165                 switch (bln->norm->Bl.type) {
1166                 case LIST_item:
1167                         outflags |= MD_br;
1168                         return 0;
1169                 case LIST_inset:
1170                 case LIST_diag:
1171                 case LIST_ohang:
1172                         outflags |= MD_br;
1173                         return 1;
1174                 case LIST_tag:
1175                 case LIST_hang:
1176                         outflags |= MD_sp;
1177                         return 1;
1178                 case LIST_bullet:
1179                         md_rawword("*\t");
1180                         break;
1181                 case LIST_dash:
1182                 case LIST_hyphen:
1183                         md_rawword("-\t");
1184                         break;
1185                 case LIST_enum:
1186                         md_preword();
1187                         if (bln->norm->Bl.count < 99)
1188                                 bln->norm->Bl.count++;
1189                         printf("%d.\t", bln->norm->Bl.count);
1190                         escflags &= ~ESC_FON;
1191                         break;
1192                 case LIST_column:
1193                         outflags |= MD_br;
1194                         return 0;
1195                 default:
1196                         return 0;
1197                 }
1198                 outflags &= ~MD_spc;
1199                 outflags |= MD_nonl;
1200                 outcount = 0;
1201                 md_stack('\t');
1202                 if (code_blocks || quote_blocks)
1203                         list_blocks++;
1204                 return 0;
1205
1206         case ROFFT_BODY:
1207                 bln = n->parent->parent;
1208                 switch (bln->norm->Bl.type) {
1209                 case LIST_ohang:
1210                         outflags |= MD_br;
1211                         break;
1212                 case LIST_tag:
1213                 case LIST_hang:
1214                         md_pre_D1(n);
1215                         break;
1216                 default:
1217                         break;
1218                 }
1219                 return 1;
1220
1221         default:
1222                 return 0;
1223         }
1224 }
1225
1226 static void
1227 md_post_It(struct roff_node *n)
1228 {
1229         struct roff_node        *bln;
1230         int                      i, nc;
1231
1232         if (n->type != ROFFT_BODY)
1233                 return;
1234
1235         bln = n->parent->parent;
1236         switch (bln->norm->Bl.type) {
1237         case LIST_bullet:
1238         case LIST_dash:
1239         case LIST_hyphen:
1240         case LIST_enum:
1241                 md_stack((char)-1);
1242                 if (code_blocks || quote_blocks)
1243                         list_blocks--;
1244                 break;
1245         case LIST_tag:
1246         case LIST_hang:
1247                 md_post_D1(n);
1248                 break;
1249
1250         case LIST_column:
1251                 if (n->next == NULL)
1252                         break;
1253
1254                 /* Calculate the array index of the current column. */
1255
1256                 i = 0;
1257                 while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1258                         i++;
1259
1260                 /* 
1261                  * If a width was specified for this column,
1262                  * subtract what printed, and
1263                  * add the same spacing as in mdoc_term.c.
1264                  */
1265
1266                 nc = bln->norm->Bl.ncols;
1267                 i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1268                     (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1269                 if (i < 1)
1270                         i = 1;
1271                 while (i-- > 0)
1272                         putchar(' ');
1273
1274                 outflags &= ~MD_spc;
1275                 escflags &= ~ESC_FON;
1276                 outcount = 0;
1277                 break;
1278
1279         default:
1280                 break;
1281         }
1282 }
1283
1284 static void
1285 md_post_Lb(struct roff_node *n)
1286 {
1287         if (n->sec == SEC_LIBRARY)
1288                 outflags |= MD_br;
1289 }
1290
1291 static void
1292 md_uri(const char *s)
1293 {
1294         while (*s != '\0') {
1295                 if (strchr("%()<>", *s) != NULL) {
1296                         printf("%%%2.2hhX", *s);
1297                         outcount += 3;
1298                 } else {
1299                         putchar(*s);
1300                         outcount++;
1301                 }
1302                 s++;
1303         }
1304 }
1305
1306 static int
1307 md_pre_Lk(struct roff_node *n)
1308 {
1309         const struct roff_node *link, *descr, *punct;
1310
1311         if ((link = n->child) == NULL)
1312                 return 0;
1313
1314         /* Find beginning of trailing punctuation. */
1315         punct = n->last;
1316         while (punct != link && punct->flags & NODE_DELIMC)
1317                 punct = punct->prev;
1318         punct = punct->next;
1319
1320         /* Link text. */
1321         descr = link->next;
1322         if (descr == punct)
1323                 descr = link;  /* no text */
1324         md_rawword("[");
1325         outflags &= ~MD_spc;
1326         do {
1327                 md_word(descr->string);
1328                 descr = descr->next;
1329         } while (descr != punct);
1330         outflags &= ~MD_spc;
1331
1332         /* Link target. */
1333         md_rawword("](");
1334         md_uri(link->string);
1335         outflags &= ~MD_spc;
1336         md_rawword(")");
1337
1338         /* Trailing punctuation. */
1339         while (punct != NULL) {
1340                 md_word(punct->string);
1341                 punct = punct->next;
1342         }
1343         return 0;
1344 }
1345
1346 static int
1347 md_pre_Mt(struct roff_node *n)
1348 {
1349         const struct roff_node *nch;
1350
1351         md_rawword("[");
1352         outflags &= ~MD_spc;
1353         for (nch = n->child; nch != NULL; nch = nch->next)
1354                 md_word(nch->string);
1355         outflags &= ~MD_spc;
1356         md_rawword("](mailto:");
1357         for (nch = n->child; nch != NULL; nch = nch->next) {
1358                 md_uri(nch->string);
1359                 if (nch->next != NULL) {
1360                         putchar(' ');
1361                         outcount++;
1362                 }
1363         }
1364         outflags &= ~MD_spc;
1365         md_rawword(")");
1366         return 0;
1367 }
1368
1369 static int
1370 md_pre_Nd(struct roff_node *n)
1371 {
1372         outflags &= ~MD_nl;
1373         outflags |= MD_spc;
1374         md_word("-");
1375         return 1;
1376 }
1377
1378 static int
1379 md_pre_Nm(struct roff_node *n)
1380 {
1381         switch (n->type) {
1382         case ROFFT_BLOCK:
1383                 outflags |= MD_Bk;
1384                 md_pre_syn(n);
1385                 break;
1386         case ROFFT_HEAD:
1387         case ROFFT_ELEM:
1388                 md_pre_raw(n);
1389                 break;
1390         default:
1391                 break;
1392         }
1393         return 1;
1394 }
1395
1396 static void
1397 md_post_Nm(struct roff_node *n)
1398 {
1399         switch (n->type) {
1400         case ROFFT_BLOCK:
1401                 outflags &= ~MD_Bk;
1402                 break;
1403         case ROFFT_HEAD:
1404         case ROFFT_ELEM:
1405                 md_post_raw(n);
1406                 break;
1407         default:
1408                 break;
1409         }
1410 }
1411
1412 static int
1413 md_pre_No(struct roff_node *n)
1414 {
1415         outflags |= MD_spc_force;
1416         return 1;
1417 }
1418
1419 static int
1420 md_pre_Ns(struct roff_node *n)
1421 {
1422         outflags &= ~MD_spc;
1423         return 0;
1424 }
1425
1426 static void
1427 md_post_Pf(struct roff_node *n)
1428 {
1429         if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1430                 outflags &= ~MD_spc;
1431 }
1432
1433 static int
1434 md_pre_Pp(struct roff_node *n)
1435 {
1436         outflags |= MD_sp;
1437         return 0;
1438 }
1439
1440 static int
1441 md_pre_Rs(struct roff_node *n)
1442 {
1443         if (n->sec == SEC_SEE_ALSO)
1444                 outflags |= MD_sp;
1445         return 1;
1446 }
1447
1448 static int
1449 md_pre_Sh(struct roff_node *n)
1450 {
1451         switch (n->type) {
1452         case ROFFT_BLOCK:
1453                 if (n->sec == SEC_AUTHORS)
1454                         outflags &= ~(MD_An_split | MD_An_nosplit);
1455                 break;
1456         case ROFFT_HEAD:
1457                 outflags |= MD_sp;
1458                 md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1459                 break;
1460         case ROFFT_BODY:
1461                 outflags |= MD_sp;
1462                 break;
1463         default:
1464                 break;
1465         }
1466         return 1;
1467 }
1468
1469 static int
1470 md_pre_Sm(struct roff_node *n)
1471 {
1472         if (n->child == NULL)
1473                 outflags ^= MD_Sm;
1474         else if (strcmp("on", n->child->string) == 0)
1475                 outflags |= MD_Sm;
1476         else
1477                 outflags &= ~MD_Sm;
1478
1479         if (outflags & MD_Sm)
1480                 outflags |= MD_spc;
1481
1482         return 0;
1483 }
1484
1485 static int
1486 md_pre_Vt(struct roff_node *n)
1487 {
1488         switch (n->type) {
1489         case ROFFT_BLOCK:
1490                 md_pre_syn(n);
1491                 return 1;
1492         case ROFFT_BODY:
1493         case ROFFT_ELEM:
1494                 md_pre_raw(n);
1495                 return 1;
1496         default:
1497                 return 0;
1498         }
1499 }
1500
1501 static void
1502 md_post_Vt(struct roff_node *n)
1503 {
1504         switch (n->type) {
1505         case ROFFT_BODY:
1506         case ROFFT_ELEM:
1507                 md_post_raw(n);
1508                 break;
1509         default:
1510                 break;
1511         }
1512 }
1513
1514 static int
1515 md_pre_Xr(struct roff_node *n)
1516 {
1517         n = n->child;
1518         if (n == NULL)
1519                 return 0;
1520         md_node(n);
1521         n = n->next;
1522         if (n == NULL)
1523                 return 0;
1524         outflags &= ~MD_spc;
1525         md_word("(");
1526         md_node(n);
1527         md_word(")");
1528         return 0;
1529 }
1530
1531 static int
1532 md_pre__T(struct roff_node *n)
1533 {
1534         if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1535                 md_word("\"");
1536         else
1537                 md_rawword("*");
1538         outflags &= ~MD_spc;
1539         return 1;
1540 }
1541
1542 static void
1543 md_post__T(struct roff_node *n)
1544 {
1545         outflags &= ~MD_spc;
1546         if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1547                 md_word("\"");
1548         else
1549                 md_rawword("*");
1550         md_post_pc(n);
1551 }
1552
1553 static int
1554 md_pre_br(struct roff_node *n)
1555 {
1556         outflags |= MD_br;
1557         return 0;
1558 }