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