1 /* $Id: mdoc_markdown.c,v 1.31 2019/07/01 22:56:24 schwarze Exp $ */
3 * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
17 #include <sys/types.h>
25 #include "mandoc_aux.h"
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 */
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 *);
49 static int md_cond_head(struct roff_node *);
50 static int md_cond_body(struct roff_node *);
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 *);
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 *);
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 */
230 static const struct md_act *md_act(enum roff_tok);
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". */
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. */
252 static int code_blocks, quote_blocks, list_blocks;
256 static const struct md_act *
257 md_act(enum roff_tok tok)
259 assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
260 return md_acts + (tok - MDOC_Dd);
264 markdown_mdoc(void *arg, const struct roff_meta *mdoc)
267 md_word(mdoc->title);
268 if (mdoc->msec != NULL) {
276 if (mdoc->arch != NULL) {
283 md_nodelist(mdoc->first->child);
293 md_nodelist(struct roff_node *n)
302 md_node(struct roff_node *n)
304 const struct md_act *act;
305 int cond, process_children;
307 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
310 if (outflags & MD_nonl)
311 outflags &= ~(MD_nl | MD_sp);
312 else if (outflags & MD_spc && n->flags & NODE_LINE)
317 process_children = 1;
318 n->flags &= ~NODE_ENDED;
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;
326 if (n->flags & NODE_DELIMO)
327 outflags &= ~(MD_spc | MD_spc_force);
328 else if (outflags & MD_Sm)
330 } else if (n->tok < ROFF_MAX) {
333 process_children = md_pre_br(n);
336 process_children = md_pre_Pp(n);
339 process_children = 0;
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);
350 if (process_children && n->child != NULL)
351 md_nodelist(n->child);
353 if (n->flags & NODE_ENDED)
356 if (cond && act->post != NULL)
359 if (n->end != ENDBODY_NOT)
360 n->body->flags |= NODE_ENDED;
380 stack = mandoc_realloc(stack, sz);
386 return stack == NULL ? "" : stack;
390 * Handle vertical and horizontal spacing.
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.
404 if (list_blocks && outflags & MD_sp) {
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.
415 if (outflags & MD_sp)
417 else if (outflags & MD_br) {
420 } else if (outflags & MD_nl && escflags & ESC_EOL)
423 /* Start a new line if necessary. */
425 if (outflags & (MD_nl | MD_br | MD_sp)) {
427 for (cp = md_stack('\0'); *cp != '\0'; cp++) {
432 outflags &= ~(MD_nl | MD_br | MD_sp);
436 /* Handle horizontal spacing. */
438 } else if (outflags & MD_spc) {
439 if (outflags & MD_Bk)
440 fputs(" ", stdout);
443 escflags &= ~ESC_FON;
447 outflags &= ~(MD_spc_force | MD_nonl);
448 if (outflags & MD_Sm)
455 * Print markdown syntax elements.
456 * Can also be used for constant strings when neither escaping
457 * nor delimiter handling is required.
460 md_rawword(const char *s)
467 if (escflags & ESC_FON) {
468 escflags &= ~ESC_FON;
469 if (*s == '*' && !code_blocks)
470 fputs("‌", stdout);
484 escflags &= ~ESC_SQU;
494 escflags &= ~ESC_EOL;
498 * Print text and mdoc(7) syntax elements.
501 md_word(const char *s)
503 const char *seq, *prevfont, *currfont, *nextfont;
505 int bs, sz, uc, breakline;
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)
518 /* No spacing after opening delimiters. */
519 if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
523 prevfont = currfont = "";
524 while ((c = *s++) != '\0') {
536 bs = escflags & ESC_BOL && !code_blocks;
544 bs = escflags & ESC_BOL && !code_blocks;
547 bs = escflags & ESC_HYP && !code_blocks;
550 bs = escflags & ESC_NUM && !code_blocks;
559 bs = escflags & ESC_NUM && !code_blocks;
562 if (code_blocks == 0) {
568 if (escflags & ESC_BOL && !code_blocks) {
574 if (code_blocks == 0) {
582 switch (mandoc_escape(&s, &seq, &sz)) {
584 uc = mchars_num2uc(seq + 1, sz - 1);
586 case ESCAPE_NUMBERED:
587 uc = mchars_num2char(seq, sz);
590 uc = mchars_spec2cp(seq, sz);
596 md_rawword("markdown");
598 case ESCAPE_FONTBOLD:
601 case ESCAPE_FONTITALIC:
609 case ESCAPE_FONTROMAN:
612 case ESCAPE_FONTPREV:
619 case ESCAPE_SKIPCHAR:
620 case ESCAPE_OVERSTRIKE:
621 /* XXX not implemented */
627 if (nextfont != NULL && !code_blocks) {
628 if (*currfont != '\0') {
630 md_rawword(currfont);
634 if (*currfont != '\0') {
636 md_rawword(currfont);
640 if ((uc < 0x20 && uc != 0x09) ||
641 (uc > 0x7E && uc < 0xA0))
644 seq = mchars_uc2str(uc);
646 outcount += strlen(seq);
651 escflags &= ~ESC_FON;
656 bs = escflags & ESC_SQU && !code_blocks;
666 (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
669 while (*s == ' ' || *s == ASCII_NBRSP)
673 if (*currfont != '\0') {
675 md_rawword(currfont);
676 } else if (s[-2] == ' ')
679 escflags &= ~ESC_EOL;
683 * Print a single HTML named character reference.
686 md_named(const char *s)
689 escflags &= ~(ESC_FON | ESC_EOL);
694 * Print a single raw character and maintain certain escape flags.
697 md_char(unsigned char c)
704 escflags &= ~ESC_FON;
708 escflags &= ~ESC_HYP;
709 if (c == ' ' || c == '\t' || c == '>')
712 escflags &= ~ESC_NUM;
713 else if (escflags & ESC_BOL)
715 escflags &= ~ESC_BOL;
719 md_cond_head(struct roff_node *n)
721 return n->type == ROFFT_HEAD;
725 md_cond_body(struct roff_node *n)
727 return n->type == ROFFT_BODY;
731 md_pre_abort(struct roff_node *n)
737 md_pre_raw(struct roff_node *n)
741 if ((prefix = md_act(n->tok)->prefix) != NULL) {
751 md_post_raw(struct roff_node *n)
755 if ((suffix = md_act(n->tok)->suffix) != NULL) {
756 outflags &= ~(MD_spc | MD_nl);
764 md_pre_word(struct roff_node *n)
768 if ((prefix = md_act(n->tok)->prefix) != NULL) {
776 md_post_word(struct roff_node *n)
780 if ((suffix = md_act(n->tok)->suffix) != NULL) {
781 outflags &= ~(MD_spc | MD_nl);
787 md_post_pc(struct roff_node *n)
790 if (n->parent->tok != MDOC_Rs)
792 if (n->next != NULL) {
794 if (n->prev != NULL &&
795 n->prev->tok == n->tok &&
796 n->next->tok == n->tok)
805 md_pre_skip(struct roff_node *n)
811 md_pre_syn(struct roff_node *n)
813 if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
816 if (n->prev->tok == n->tok &&
824 switch (n->prev->tok) {
833 if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
845 md_pre_An(struct roff_node *n)
847 switch (n->norm->An.auth) {
849 outflags &= ~MD_An_nosplit;
850 outflags |= MD_An_split;
853 outflags &= ~MD_An_split;
854 outflags |= MD_An_nosplit;
857 if (outflags & MD_An_split)
859 else if (n->sec == SEC_AUTHORS &&
860 ! (outflags & MD_An_nosplit))
861 outflags |= MD_An_split;
867 md_pre_Ap(struct roff_node *n)
876 md_pre_Bd(struct roff_node *n)
878 switch (n->norm->Bd.type) {
888 md_pre_Bk(struct roff_node *n)
902 md_post_Bk(struct roff_node *n)
904 if (n->type == ROFFT_BODY)
909 md_pre_Bl(struct roff_node *n)
911 n->norm->Bl.count = 0;
912 if (n->norm->Bl.type == LIST_column)
919 md_post_Bl(struct roff_node *n)
921 n->norm->Bl.count = 0;
922 if (n->norm->Bl.type == LIST_column)
928 md_pre_D1(struct roff_node *n)
931 * Markdown blockquote syntax does not work inside code blocks.
932 * The best we can do is fall back to another nested code block.
946 md_post_D1(struct roff_node *n)
957 md_pre_Dl(struct roff_node *n)
960 * Markdown code block syntax does not work inside blockquotes.
961 * The best we can do is fall back to another nested blockquote.
975 md_pre_En(struct roff_node *n)
977 if (n->norm->Es == NULL ||
978 n->norm->Es->child == NULL)
981 md_word(n->norm->Es->child->string);
987 md_post_En(struct roff_node *n)
989 if (n->norm->Es == NULL ||
990 n->norm->Es->child == NULL ||
991 n->norm->Es->child->next == NULL)
995 md_word(n->norm->Es->child->next->string);
999 md_pre_Eo(struct roff_node *n)
1001 if (n->end == ENDBODY_NOT &&
1002 n->parent->head->child == NULL &&
1004 n->child->end != ENDBODY_NOT)
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);
1014 md_post_Eo(struct roff_node *n)
1016 if (n->end != ENDBODY_NOT) {
1021 if (n->child == NULL && n->parent->head->child == NULL)
1024 if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1025 outflags &= ~MD_spc;
1031 md_pre_Fa(struct roff_node *n)
1035 am_Fa = n->tok == MDOC_Fa;
1042 outflags &= ~MD_spc;
1044 outflags &= ~MD_spc;
1046 if ((n = n->next) != NULL)
1053 md_post_Fa(struct roff_node *n)
1055 if (n->next != NULL && n->next->tok == MDOC_Fa)
1060 md_pre_Fd(struct roff_node *n)
1068 md_post_Fd(struct roff_node *n)
1075 md_post_Fl(struct roff_node *n)
1078 if (n->child == NULL && n->next != NULL &&
1079 n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1080 outflags &= ~MD_spc;
1084 md_pre_Fn(struct roff_node *n)
1088 if ((n = n->child) == NULL)
1092 outflags &= ~MD_spc;
1094 outflags &= ~MD_spc;
1096 outflags &= ~MD_spc;
1099 if ((n = n->next) != NULL)
1105 md_post_Fn(struct roff_node *n)
1108 if (n->flags & NODE_SYNPRETTY) {
1115 md_pre_Fo(struct roff_node *n)
1122 if (n->child == NULL)
1127 outflags &= ~(MD_spc | MD_nl);
1137 md_post_Fo(struct roff_node *n)
1141 if (n->child != NULL)
1153 md_pre_In(struct roff_node *n)
1155 if (n->flags & NODE_SYNPRETTY) {
1158 outflags &= ~MD_spc;
1159 md_word("#include <");
1162 outflags &= ~MD_spc;
1165 outflags &= ~MD_spc;
1170 md_post_In(struct roff_node *n)
1172 if (n->flags & NODE_SYNPRETTY) {
1173 outflags &= ~MD_spc;
1177 outflags &= ~MD_spc;
1183 md_pre_It(struct roff_node *n)
1185 struct roff_node *bln;
1192 bln = n->parent->parent;
1193 if (bln->norm->Bl.comp == 0 &&
1194 bln->norm->Bl.type != LIST_column)
1198 switch (bln->norm->Bl.type) {
1220 if (bln->norm->Bl.count < 99)
1221 bln->norm->Bl.count++;
1222 printf("%d.\t", bln->norm->Bl.count);
1223 escflags &= ~ESC_FON;
1231 outflags &= ~MD_spc;
1232 outflags |= MD_nonl;
1235 if (code_blocks || quote_blocks)
1240 bln = n->parent->parent;
1241 switch (bln->norm->Bl.type) {
1260 md_post_It(struct roff_node *n)
1262 struct roff_node *bln;
1265 if (n->type != ROFFT_BODY)
1268 bln = n->parent->parent;
1269 switch (bln->norm->Bl.type) {
1275 if (code_blocks || quote_blocks)
1284 if (n->next == NULL)
1287 /* Calculate the array index of the current column. */
1290 while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1294 * If a width was specified for this column,
1295 * subtract what printed, and
1296 * add the same spacing as in mdoc_term.c.
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;
1307 outflags &= ~MD_spc;
1308 escflags &= ~ESC_FON;
1318 md_post_Lb(struct roff_node *n)
1320 if (n->sec == SEC_LIBRARY)
1325 md_uri(const char *s)
1327 while (*s != '\0') {
1328 if (strchr("%()<>", *s) != NULL) {
1329 printf("%%%2.2hhX", *s);
1340 md_pre_Lk(struct roff_node *n)
1342 const struct roff_node *link, *descr, *punct;
1344 if ((link = n->child) == NULL)
1347 /* Find beginning of trailing punctuation. */
1349 while (punct != link && punct->flags & NODE_DELIMC)
1350 punct = punct->prev;
1351 punct = punct->next;
1356 descr = link; /* no text */
1358 outflags &= ~MD_spc;
1360 md_word(descr->string);
1361 descr = descr->next;
1362 } while (descr != punct);
1363 outflags &= ~MD_spc;
1367 md_uri(link->string);
1368 outflags &= ~MD_spc;
1371 /* Trailing punctuation. */
1372 while (punct != NULL) {
1373 md_word(punct->string);
1374 punct = punct->next;
1380 md_pre_Mt(struct roff_node *n)
1382 const struct roff_node *nch;
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) {
1397 outflags &= ~MD_spc;
1403 md_pre_Nd(struct roff_node *n)
1412 md_pre_Nm(struct roff_node *n)
1430 md_post_Nm(struct roff_node *n)
1446 md_pre_No(struct roff_node *n)
1448 outflags |= MD_spc_force;
1453 md_pre_Ns(struct roff_node *n)
1455 outflags &= ~MD_spc;
1460 md_post_Pf(struct roff_node *n)
1462 if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1463 outflags &= ~MD_spc;
1467 md_pre_Pp(struct roff_node *n)
1474 md_pre_Rs(struct roff_node *n)
1476 if (n->sec == SEC_SEE_ALSO)
1482 md_pre_Sh(struct roff_node *n)
1486 if (n->sec == SEC_AUTHORS)
1487 outflags &= ~(MD_An_split | MD_An_nosplit);
1491 md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1503 md_pre_Sm(struct roff_node *n)
1505 if (n->child == NULL)
1507 else if (strcmp("on", n->child->string) == 0)
1512 if (outflags & MD_Sm)
1519 md_pre_Vt(struct roff_node *n)
1535 md_post_Vt(struct roff_node *n)
1548 md_pre_Xr(struct roff_node *n)
1557 outflags &= ~MD_spc;
1565 md_pre__T(struct roff_node *n)
1567 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1571 outflags &= ~MD_spc;
1576 md_post__T(struct roff_node *n)
1578 outflags &= ~MD_spc;
1579 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1587 md_pre_br(struct roff_node *n)