]>
git.cameronkatri.com Git - mandoc.git/blob - mdoc_markdown.c
1 /* $Id: mdoc_markdown.c,v 1.36 2020/06/22 19:20:40 schwarze Exp $ */
3 * Copyright (c) 2017, 2018, 2020 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 * Markdown formatter for mdoc(7) used by mandoc(1).
21 #include <sys/types.h>
29 #include "mandoc_aux.h"
36 int (*cond
)(struct roff_node
*);
37 int (*pre
)(struct roff_node
*);
38 void (*post
)(struct roff_node
*);
39 const char *prefix
; /* pre-node string constant */
40 const char *suffix
; /* post-node string constant */
43 static void md_nodelist(struct roff_node
*);
44 static void md_node(struct roff_node
*);
45 static const char *md_stack(char);
46 static void md_preword(void);
47 static void md_rawword(const char *);
48 static void md_word(const char *);
49 static void md_named(const char *);
50 static void md_char(unsigned char);
51 static void md_uri(const char *);
53 static int md_cond_head(struct roff_node
*);
54 static int md_cond_body(struct roff_node
*);
56 static int md_pre_abort(struct roff_node
*);
57 static int md_pre_raw(struct roff_node
*);
58 static int md_pre_word(struct roff_node
*);
59 static int md_pre_skip(struct roff_node
*);
60 static void md_pre_syn(struct roff_node
*);
61 static int md_pre_An(struct roff_node
*);
62 static int md_pre_Ap(struct roff_node
*);
63 static int md_pre_Bd(struct roff_node
*);
64 static int md_pre_Bk(struct roff_node
*);
65 static int md_pre_Bl(struct roff_node
*);
66 static int md_pre_D1(struct roff_node
*);
67 static int md_pre_Dl(struct roff_node
*);
68 static int md_pre_En(struct roff_node
*);
69 static int md_pre_Eo(struct roff_node
*);
70 static int md_pre_Fa(struct roff_node
*);
71 static int md_pre_Fd(struct roff_node
*);
72 static int md_pre_Fn(struct roff_node
*);
73 static int md_pre_Fo(struct roff_node
*);
74 static int md_pre_In(struct roff_node
*);
75 static int md_pre_It(struct roff_node
*);
76 static int md_pre_Lk(struct roff_node
*);
77 static int md_pre_Mt(struct roff_node
*);
78 static int md_pre_Nd(struct roff_node
*);
79 static int md_pre_Nm(struct roff_node
*);
80 static int md_pre_No(struct roff_node
*);
81 static int md_pre_Ns(struct roff_node
*);
82 static int md_pre_Pp(struct roff_node
*);
83 static int md_pre_Rs(struct roff_node
*);
84 static int md_pre_Sh(struct roff_node
*);
85 static int md_pre_Sm(struct roff_node
*);
86 static int md_pre_Vt(struct roff_node
*);
87 static int md_pre_Xr(struct roff_node
*);
88 static int md_pre__T(struct roff_node
*);
89 static int md_pre_br(struct roff_node
*);
91 static void md_post_raw(struct roff_node
*);
92 static void md_post_word(struct roff_node
*);
93 static void md_post_pc(struct roff_node
*);
94 static void md_post_Bk(struct roff_node
*);
95 static void md_post_Bl(struct roff_node
*);
96 static void md_post_D1(struct roff_node
*);
97 static void md_post_En(struct roff_node
*);
98 static void md_post_Eo(struct roff_node
*);
99 static void md_post_Fa(struct roff_node
*);
100 static void md_post_Fd(struct roff_node
*);
101 static void md_post_Fl(struct roff_node
*);
102 static void md_post_Fn(struct roff_node
*);
103 static void md_post_Fo(struct roff_node
*);
104 static void md_post_In(struct roff_node
*);
105 static void md_post_It(struct roff_node
*);
106 static void md_post_Lb(struct roff_node
*);
107 static void md_post_Nm(struct roff_node
*);
108 static void md_post_Pf(struct roff_node
*);
109 static void md_post_Vt(struct roff_node
*);
110 static void md_post__T(struct roff_node
*);
112 static const struct md_act md_acts
[MDOC_MAX
- MDOC_Dd
] = {
113 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Dd */
114 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Dt */
115 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Os */
116 { NULL
, md_pre_Sh
, NULL
, NULL
, NULL
}, /* Sh */
117 { NULL
, md_pre_Sh
, NULL
, NULL
, NULL
}, /* Ss */
118 { NULL
, md_pre_Pp
, NULL
, NULL
, NULL
}, /* Pp */
119 { md_cond_body
, md_pre_D1
, md_post_D1
, NULL
, NULL
}, /* D1 */
120 { md_cond_body
, md_pre_Dl
, md_post_D1
, NULL
, NULL
}, /* Dl */
121 { md_cond_body
, md_pre_Bd
, md_post_D1
, NULL
, NULL
}, /* Bd */
122 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ed */
123 { md_cond_body
, md_pre_Bl
, md_post_Bl
, NULL
, NULL
}, /* Bl */
124 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* El */
125 { NULL
, md_pre_It
, md_post_It
, NULL
, NULL
}, /* It */
126 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Ad */
127 { NULL
, md_pre_An
, NULL
, NULL
, NULL
}, /* An */
128 { NULL
, md_pre_Ap
, NULL
, NULL
, NULL
}, /* Ap */
129 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Ar */
130 { NULL
, md_pre_raw
, md_post_raw
, "**", "**" }, /* Cd */
131 { NULL
, md_pre_raw
, md_post_raw
, "**", "**" }, /* Cm */
132 { NULL
, md_pre_raw
, md_post_raw
, "`", "`" }, /* Dv */
133 { NULL
, md_pre_raw
, md_post_raw
, "`", "`" }, /* Er */
134 { NULL
, md_pre_raw
, md_post_raw
, "`", "`" }, /* Ev */
135 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ex */
136 { NULL
, md_pre_Fa
, md_post_Fa
, NULL
, NULL
}, /* Fa */
137 { NULL
, md_pre_Fd
, md_post_Fd
, "**", "**" }, /* Fd */
138 { NULL
, md_pre_raw
, md_post_Fl
, "**-", "**" }, /* Fl */
139 { NULL
, md_pre_Fn
, md_post_Fn
, NULL
, NULL
}, /* Fn */
140 { NULL
, md_pre_Fd
, md_post_raw
, "*", "*" }, /* Ft */
141 { NULL
, md_pre_raw
, md_post_raw
, "**", "**" }, /* Ic */
142 { NULL
, md_pre_In
, md_post_In
, NULL
, NULL
}, /* In */
143 { NULL
, md_pre_raw
, md_post_raw
, "`", "`" }, /* Li */
144 { md_cond_head
, md_pre_Nd
, NULL
, NULL
, NULL
}, /* Nd */
145 { NULL
, md_pre_Nm
, md_post_Nm
, "**", "**" }, /* Nm */
146 { md_cond_body
, md_pre_word
, md_post_word
, "[", "]" }, /* Op */
147 { NULL
, md_pre_abort
, NULL
, NULL
, NULL
}, /* Ot */
148 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Pa */
149 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Rv */
150 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* St */
151 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Va */
152 { NULL
, md_pre_Vt
, md_post_Vt
, "*", "*" }, /* Vt */
153 { NULL
, md_pre_Xr
, NULL
, NULL
, NULL
}, /* Xr */
154 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %A */
155 { NULL
, md_pre_raw
, md_post_pc
, "*", "*" }, /* %B */
156 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %D */
157 { NULL
, md_pre_raw
, md_post_pc
, "*", "*" }, /* %I */
158 { NULL
, md_pre_raw
, md_post_pc
, "*", "*" }, /* %J */
159 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %N */
160 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %O */
161 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %P */
162 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %R */
163 { NULL
, md_pre__T
, md_post__T
, NULL
, NULL
}, /* %T */
164 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %V */
165 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ac */
166 { md_cond_body
, md_pre_word
, md_post_word
, "<", ">" }, /* Ao */
167 { md_cond_body
, md_pre_word
, md_post_word
, "<", ">" }, /* Aq */
168 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* At */
169 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Bc */
170 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Bf XXX not implemented */
171 { md_cond_body
, md_pre_word
, md_post_word
, "[", "]" }, /* Bo */
172 { md_cond_body
, md_pre_word
, md_post_word
, "[", "]" }, /* Bq */
173 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Bsx */
174 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Bx */
175 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Db */
176 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Dc */
177 { md_cond_body
, md_pre_word
, md_post_word
, "\"", "\"" }, /* Do */
178 { md_cond_body
, md_pre_word
, md_post_word
, "\"", "\"" }, /* Dq */
179 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ec */
180 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ef */
181 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Em */
182 { md_cond_body
, md_pre_Eo
, md_post_Eo
, NULL
, NULL
}, /* Eo */
183 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Fx */
184 { NULL
, md_pre_raw
, md_post_raw
, "**", "**" }, /* Ms */
185 { NULL
, md_pre_No
, NULL
, NULL
, NULL
}, /* No */
186 { NULL
, md_pre_Ns
, NULL
, NULL
, NULL
}, /* Ns */
187 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Nx */
188 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ox */
189 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Pc */
190 { NULL
, NULL
, md_post_Pf
, NULL
, NULL
}, /* Pf */
191 { md_cond_body
, md_pre_word
, md_post_word
, "(", ")" }, /* Po */
192 { md_cond_body
, md_pre_word
, md_post_word
, "(", ")" }, /* Pq */
193 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Qc */
194 { md_cond_body
, md_pre_raw
, md_post_raw
, "'`", "`'" }, /* Ql */
195 { md_cond_body
, md_pre_word
, md_post_word
, "\"", "\"" }, /* Qo */
196 { md_cond_body
, md_pre_word
, md_post_word
, "\"", "\"" }, /* Qq */
197 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Re */
198 { md_cond_body
, md_pre_Rs
, NULL
, NULL
, NULL
}, /* Rs */
199 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Sc */
200 { md_cond_body
, md_pre_word
, md_post_word
, "'", "'" }, /* So */
201 { md_cond_body
, md_pre_word
, md_post_word
, "'", "'" }, /* Sq */
202 { NULL
, md_pre_Sm
, NULL
, NULL
, NULL
}, /* Sm */
203 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Sx */
204 { NULL
, md_pre_raw
, md_post_raw
, "**", "**" }, /* Sy */
205 { NULL
, md_pre_raw
, md_post_raw
, "`", "`" }, /* Tn */
206 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ux */
207 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Xc */
208 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Xo */
209 { NULL
, md_pre_Fo
, md_post_Fo
, "**", "**" }, /* Fo */
210 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Fc */
211 { md_cond_body
, md_pre_word
, md_post_word
, "[", "]" }, /* Oo */
212 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Oc */
213 { NULL
, md_pre_Bk
, md_post_Bk
, NULL
, NULL
}, /* Bk */
214 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ek */
215 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Bt */
216 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Hf */
217 { NULL
, md_pre_raw
, md_post_raw
, "*", "*" }, /* Fr */
218 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ud */
219 { NULL
, NULL
, md_post_Lb
, NULL
, NULL
}, /* Lb */
220 { NULL
, md_pre_abort
, NULL
, NULL
, NULL
}, /* Lp */
221 { NULL
, md_pre_Lk
, NULL
, NULL
, NULL
}, /* Lk */
222 { NULL
, md_pre_Mt
, NULL
, NULL
, NULL
}, /* Mt */
223 { md_cond_body
, md_pre_word
, md_post_word
, "{", "}" }, /* Brq */
224 { md_cond_body
, md_pre_word
, md_post_word
, "{", "}" }, /* Bro */
225 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Brc */
226 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %C */
227 { NULL
, md_pre_skip
, NULL
, NULL
, NULL
}, /* Es */
228 { md_cond_body
, md_pre_En
, md_post_En
, NULL
, NULL
}, /* En */
229 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Dx */
230 { NULL
, NULL
, md_post_pc
, NULL
, NULL
}, /* %Q */
231 { NULL
, md_pre_Lk
, md_post_pc
, NULL
, NULL
}, /* %U */
232 { NULL
, NULL
, NULL
, NULL
, NULL
}, /* Ta */
233 { NULL
, md_pre_skip
, NULL
, NULL
, NULL
}, /* Tg */
235 static const struct md_act
*md_act(enum roff_tok
);
238 #define MD_spc (1 << 0) /* Blank character before next word. */
239 #define MD_spc_force (1 << 1) /* Even before trailing punctuation. */
240 #define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */
241 #define MD_nl (1 << 3) /* Break markdown code line. */
242 #define MD_br (1 << 4) /* Insert an output line break. */
243 #define MD_sp (1 << 5) /* Insert a paragraph break. */
244 #define MD_Sm (1 << 6) /* Horizontal spacing mode. */
245 #define MD_Bk (1 << 7) /* Word keep mode. */
246 #define MD_An_split (1 << 8) /* Author mode is "split". */
247 #define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */
249 static int escflags
; /* Escape in generated markdown code: */
250 #define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */
251 #define ESC_NUM (1 << 1) /* "." after a leading number. */
252 #define ESC_HYP (1 << 2) /* "(" immediately after "]". */
253 #define ESC_SQU (1 << 4) /* "]" when "[" is open. */
254 #define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */
255 #define ESC_EOL (1 << 6) /* " " at the and of a line. */
257 static int code_blocks
, quote_blocks
, list_blocks
;
261 static const struct md_act
*
262 md_act(enum roff_tok tok
)
264 assert(tok
>= MDOC_Dd
&& tok
<= MDOC_MAX
);
265 return md_acts
+ (tok
- MDOC_Dd
);
269 markdown_mdoc(void *arg
, const struct roff_meta
*mdoc
)
272 md_word(mdoc
->title
);
273 if (mdoc
->msec
!= NULL
) {
281 if (mdoc
->arch
!= NULL
) {
288 md_nodelist(mdoc
->first
->child
);
298 md_nodelist(struct roff_node
*n
)
307 md_node(struct roff_node
*n
)
309 const struct md_act
*act
;
310 int cond
, process_children
;
312 if (n
->type
== ROFFT_COMMENT
|| n
->flags
& NODE_NOPRT
)
315 if (outflags
& MD_nonl
)
316 outflags
&= ~(MD_nl
| MD_sp
);
317 else if (outflags
& MD_spc
&&
318 n
->flags
& NODE_LINE
&&
319 !roff_node_transparent(n
))
324 process_children
= 1;
325 n
->flags
&= ~NODE_ENDED
;
327 if (n
->type
== ROFFT_TEXT
) {
328 if (n
->flags
& NODE_DELIMC
)
329 outflags
&= ~(MD_spc
| MD_spc_force
);
330 else if (outflags
& MD_Sm
)
331 outflags
|= MD_spc_force
;
333 if (n
->flags
& NODE_DELIMO
)
334 outflags
&= ~(MD_spc
| MD_spc_force
);
335 else if (outflags
& MD_Sm
)
337 } else if (n
->tok
< ROFF_MAX
) {
340 process_children
= md_pre_br(n
);
343 process_children
= md_pre_Pp(n
);
346 process_children
= 0;
350 act
= md_act(n
->tok
);
351 cond
= act
->cond
== NULL
|| (*act
->cond
)(n
);
352 if (cond
&& act
->pre
!= NULL
&&
353 (n
->end
== ENDBODY_NOT
|| n
->child
!= NULL
))
354 process_children
= (*act
->pre
)(n
);
357 if (process_children
&& n
->child
!= NULL
)
358 md_nodelist(n
->child
);
360 if (n
->flags
& NODE_ENDED
)
363 if (cond
&& act
->post
!= NULL
)
366 if (n
->end
!= ENDBODY_NOT
)
367 n
->body
->flags
|= NODE_ENDED
;
387 stack
= mandoc_realloc(stack
, sz
);
393 return stack
== NULL
? "" : stack
;
397 * Handle vertical and horizontal spacing.
405 * If a list block is nested inside a code block or a blockquote,
406 * blank lines for paragraph breaks no longer work; instead,
407 * they terminate the list. Work around this markdown issue
408 * by using mere line breaks instead.
411 if (list_blocks
&& outflags
& MD_sp
) {
417 * End the old line if requested.
418 * Escape whitespace at the end of the markdown line
419 * such that it won't look like an output line break.
422 if (outflags
& MD_sp
)
424 else if (outflags
& MD_br
) {
427 } else if (outflags
& MD_nl
&& escflags
& ESC_EOL
)
430 /* Start a new line if necessary. */
432 if (outflags
& (MD_nl
| MD_br
| MD_sp
)) {
434 for (cp
= md_stack('\0'); *cp
!= '\0'; cp
++) {
439 outflags
&= ~(MD_nl
| MD_br
| MD_sp
);
443 /* Handle horizontal spacing. */
445 } else if (outflags
& MD_spc
) {
446 if (outflags
& MD_Bk
)
447 fputs(" ", stdout
);
450 escflags
&= ~ESC_FON
;
454 outflags
&= ~(MD_spc_force
| MD_nonl
);
455 if (outflags
& MD_Sm
)
462 * Print markdown syntax elements.
463 * Can also be used for constant strings when neither escaping
464 * nor delimiter handling is required.
467 md_rawword(const char *s
)
474 if (escflags
& ESC_FON
) {
475 escflags
&= ~ESC_FON
;
476 if (*s
== '*' && !code_blocks
)
477 fputs("‌", stdout
);
491 escflags
&= ~ESC_SQU
;
501 escflags
&= ~ESC_EOL
;
505 * Print text and mdoc(7) syntax elements.
508 md_word(const char *s
)
510 const char *seq
, *prevfont
, *currfont
, *nextfont
;
512 int bs
, sz
, uc
, breakline
;
514 /* No spacing before closing delimiters. */
515 if (s
[0] != '\0' && s
[1] == '\0' &&
516 strchr("!),.:;?]", s
[0]) != NULL
&&
517 (outflags
& MD_spc_force
) == 0)
525 /* No spacing after opening delimiters. */
526 if ((s
[0] == '(' || s
[0] == '[') && s
[1] == '\0')
530 prevfont
= currfont
= "";
531 while ((c
= *s
++) != '\0') {
543 bs
= escflags
& ESC_BOL
&& !code_blocks
;
551 bs
= escflags
& ESC_BOL
&& !code_blocks
;
554 bs
= escflags
& ESC_HYP
&& !code_blocks
;
557 bs
= escflags
& ESC_NUM
&& !code_blocks
;
566 bs
= escflags
& ESC_NUM
&& !code_blocks
;
569 if (code_blocks
== 0) {
575 if (escflags
& ESC_BOL
&& !code_blocks
) {
581 if (code_blocks
== 0) {
589 switch (mandoc_escape(&s
, &seq
, &sz
)) {
591 uc
= mchars_num2uc(seq
+ 1, sz
- 1);
593 case ESCAPE_NUMBERED
:
594 uc
= mchars_num2char(seq
, sz
);
597 uc
= mchars_spec2cp(seq
, sz
);
603 md_rawword("markdown");
605 case ESCAPE_FONTBOLD
:
608 case ESCAPE_FONTITALIC
:
616 case ESCAPE_FONTROMAN
:
619 case ESCAPE_FONTPREV
:
626 case ESCAPE_SKIPCHAR
:
627 case ESCAPE_OVERSTRIKE
:
628 /* XXX not implemented */
634 if (nextfont
!= NULL
&& !code_blocks
) {
635 if (*currfont
!= '\0') {
637 md_rawword(currfont
);
641 if (*currfont
!= '\0') {
643 md_rawword(currfont
);
647 if ((uc
< 0x20 && uc
!= 0x09) ||
648 (uc
> 0x7E && uc
< 0xA0))
651 seq
= mchars_uc2str(uc
);
653 outcount
+= strlen(seq
);
658 escflags
&= ~ESC_FON
;
663 bs
= escflags
& ESC_SQU
&& !code_blocks
;
673 (*s
== '\0' || *s
== ' ' || *s
== ASCII_NBRSP
)) {
676 while (*s
== ' ' || *s
== ASCII_NBRSP
)
680 if (*currfont
!= '\0') {
682 md_rawword(currfont
);
683 } else if (s
[-2] == ' ')
686 escflags
&= ~ESC_EOL
;
690 * Print a single HTML named character reference.
693 md_named(const char *s
)
696 escflags
&= ~(ESC_FON
| ESC_EOL
);
701 * Print a single raw character and maintain certain escape flags.
704 md_char(unsigned char c
)
711 escflags
&= ~ESC_FON
;
715 escflags
&= ~ESC_HYP
;
716 if (c
== ' ' || c
== '\t' || c
== '>')
719 escflags
&= ~ESC_NUM
;
720 else if (escflags
& ESC_BOL
)
722 escflags
&= ~ESC_BOL
;
726 md_cond_head(struct roff_node
*n
)
728 return n
->type
== ROFFT_HEAD
;
732 md_cond_body(struct roff_node
*n
)
734 return n
->type
== ROFFT_BODY
;
738 md_pre_abort(struct roff_node
*n
)
744 md_pre_raw(struct roff_node
*n
)
748 if ((prefix
= md_act(n
->tok
)->prefix
) != NULL
) {
758 md_post_raw(struct roff_node
*n
)
762 if ((suffix
= md_act(n
->tok
)->suffix
) != NULL
) {
763 outflags
&= ~(MD_spc
| MD_nl
);
771 md_pre_word(struct roff_node
*n
)
775 if ((prefix
= md_act(n
->tok
)->prefix
) != NULL
) {
783 md_post_word(struct roff_node
*n
)
787 if ((suffix
= md_act(n
->tok
)->suffix
) != NULL
) {
788 outflags
&= ~(MD_spc
| MD_nl
);
794 md_post_pc(struct roff_node
*n
)
796 struct roff_node
*nn
;
799 if (n
->parent
->tok
!= MDOC_Rs
)
802 if ((nn
= roff_node_next(n
)) != NULL
) {
804 if (nn
->tok
== n
->tok
&&
805 (nn
= roff_node_prev(n
)) != NULL
&&
815 md_pre_skip(struct roff_node
*n
)
821 md_pre_syn(struct roff_node
*n
)
823 struct roff_node
*np
;
825 if ((n
->flags
& NODE_SYNPRETTY
) == 0 ||
826 (np
= roff_node_prev(n
)) == NULL
)
829 if (np
->tok
== n
->tok
&&
846 if (n
->tok
!= MDOC_Fn
&& n
->tok
!= MDOC_Fo
) {
858 md_pre_An(struct roff_node
*n
)
860 switch (n
->norm
->An
.auth
) {
862 outflags
&= ~MD_An_nosplit
;
863 outflags
|= MD_An_split
;
866 outflags
&= ~MD_An_split
;
867 outflags
|= MD_An_nosplit
;
870 if (outflags
& MD_An_split
)
872 else if (n
->sec
== SEC_AUTHORS
&&
873 ! (outflags
& MD_An_nosplit
))
874 outflags
|= MD_An_split
;
880 md_pre_Ap(struct roff_node
*n
)
889 md_pre_Bd(struct roff_node
*n
)
891 switch (n
->norm
->Bd
.type
) {
901 md_pre_Bk(struct roff_node
*n
)
915 md_post_Bk(struct roff_node
*n
)
917 if (n
->type
== ROFFT_BODY
)
922 md_pre_Bl(struct roff_node
*n
)
924 n
->norm
->Bl
.count
= 0;
925 if (n
->norm
->Bl
.type
== LIST_column
)
932 md_post_Bl(struct roff_node
*n
)
934 n
->norm
->Bl
.count
= 0;
935 if (n
->norm
->Bl
.type
== LIST_column
)
941 md_pre_D1(struct roff_node
*n
)
944 * Markdown blockquote syntax does not work inside code blocks.
945 * The best we can do is fall back to another nested code block.
959 md_post_D1(struct roff_node
*n
)
970 md_pre_Dl(struct roff_node
*n
)
973 * Markdown code block syntax does not work inside blockquotes.
974 * The best we can do is fall back to another nested blockquote.
988 md_pre_En(struct roff_node
*n
)
990 if (n
->norm
->Es
== NULL
||
991 n
->norm
->Es
->child
== NULL
)
994 md_word(n
->norm
->Es
->child
->string
);
1000 md_post_En(struct roff_node
*n
)
1002 if (n
->norm
->Es
== NULL
||
1003 n
->norm
->Es
->child
== NULL
||
1004 n
->norm
->Es
->child
->next
== NULL
)
1007 outflags
&= ~MD_spc
;
1008 md_word(n
->norm
->Es
->child
->next
->string
);
1012 md_pre_Eo(struct roff_node
*n
)
1014 if (n
->end
== ENDBODY_NOT
&&
1015 n
->parent
->head
->child
== NULL
&&
1017 n
->child
->end
!= ENDBODY_NOT
)
1019 else if (n
->end
!= ENDBODY_NOT
? n
->child
!= NULL
:
1020 n
->parent
->head
->child
!= NULL
&& (n
->child
!= NULL
||
1021 (n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
)))
1022 outflags
&= ~(MD_spc
| MD_nl
);
1027 md_post_Eo(struct roff_node
*n
)
1029 if (n
->end
!= ENDBODY_NOT
) {
1034 if (n
->child
== NULL
&& n
->parent
->head
->child
== NULL
)
1037 if (n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
)
1038 outflags
&= ~MD_spc
;
1044 md_pre_Fa(struct roff_node
*n
)
1048 am_Fa
= n
->tok
== MDOC_Fa
;
1055 outflags
&= ~MD_spc
;
1057 outflags
&= ~MD_spc
;
1059 if ((n
= n
->next
) != NULL
)
1066 md_post_Fa(struct roff_node
*n
)
1068 struct roff_node
*nn
;
1070 if ((nn
= roff_node_next(n
)) != NULL
&& nn
->tok
== MDOC_Fa
)
1075 md_pre_Fd(struct roff_node
*n
)
1083 md_post_Fd(struct roff_node
*n
)
1090 md_post_Fl(struct roff_node
*n
)
1092 struct roff_node
*nn
;
1095 if (n
->child
== NULL
&& (nn
= roff_node_next(n
)) != NULL
&&
1096 nn
->type
!= ROFFT_TEXT
&& (nn
->flags
& NODE_LINE
) == 0)
1097 outflags
&= ~MD_spc
;
1101 md_pre_Fn(struct roff_node
*n
)
1105 if ((n
= n
->child
) == NULL
)
1109 outflags
&= ~MD_spc
;
1111 outflags
&= ~MD_spc
;
1113 outflags
&= ~MD_spc
;
1116 if ((n
= n
->next
) != NULL
)
1122 md_post_Fn(struct roff_node
*n
)
1125 if (n
->flags
& NODE_SYNPRETTY
) {
1132 md_pre_Fo(struct roff_node
*n
)
1139 if (n
->child
== NULL
)
1144 outflags
&= ~(MD_spc
| MD_nl
);
1154 md_post_Fo(struct roff_node
*n
)
1158 if (n
->child
!= NULL
)
1170 md_pre_In(struct roff_node
*n
)
1172 if (n
->flags
& NODE_SYNPRETTY
) {
1175 outflags
&= ~MD_spc
;
1176 md_word("#include <");
1179 outflags
&= ~MD_spc
;
1182 outflags
&= ~MD_spc
;
1187 md_post_In(struct roff_node
*n
)
1189 if (n
->flags
& NODE_SYNPRETTY
) {
1190 outflags
&= ~MD_spc
;
1194 outflags
&= ~MD_spc
;
1200 md_pre_It(struct roff_node
*n
)
1202 struct roff_node
*bln
;
1209 bln
= n
->parent
->parent
;
1210 if (bln
->norm
->Bl
.comp
== 0 &&
1211 bln
->norm
->Bl
.type
!= LIST_column
)
1215 switch (bln
->norm
->Bl
.type
) {
1237 if (bln
->norm
->Bl
.count
< 99)
1238 bln
->norm
->Bl
.count
++;
1239 printf("%d.\t", bln
->norm
->Bl
.count
);
1240 escflags
&= ~ESC_FON
;
1248 outflags
&= ~MD_spc
;
1249 outflags
|= MD_nonl
;
1252 if (code_blocks
|| quote_blocks
)
1257 bln
= n
->parent
->parent
;
1258 switch (bln
->norm
->Bl
.type
) {
1277 md_post_It(struct roff_node
*n
)
1279 struct roff_node
*bln
;
1282 if (n
->type
!= ROFFT_BODY
)
1285 bln
= n
->parent
->parent
;
1286 switch (bln
->norm
->Bl
.type
) {
1292 if (code_blocks
|| quote_blocks
)
1301 if (n
->next
== NULL
)
1304 /* Calculate the array index of the current column. */
1307 while ((n
= n
->prev
) != NULL
&& n
->type
!= ROFFT_HEAD
)
1311 * If a width was specified for this column,
1312 * subtract what printed, and
1313 * add the same spacing as in mdoc_term.c.
1316 nc
= bln
->norm
->Bl
.ncols
;
1317 i
= i
< nc
? strlen(bln
->norm
->Bl
.cols
[i
]) - outcount
+
1318 (nc
< 5 ? 4 : nc
== 5 ? 3 : 1) : 1;
1324 outflags
&= ~MD_spc
;
1325 escflags
&= ~ESC_FON
;
1335 md_post_Lb(struct roff_node
*n
)
1337 if (n
->sec
== SEC_LIBRARY
)
1342 md_uri(const char *s
)
1344 while (*s
!= '\0') {
1345 if (strchr("%()<>", *s
) != NULL
) {
1346 printf("%%%2.2hhX", *s
);
1357 md_pre_Lk(struct roff_node
*n
)
1359 const struct roff_node
*link
, *descr
, *punct
;
1361 if ((link
= n
->child
) == NULL
)
1364 /* Find beginning of trailing punctuation. */
1366 while (punct
!= link
&& punct
->flags
& NODE_DELIMC
)
1367 punct
= punct
->prev
;
1368 punct
= punct
->next
;
1373 descr
= link
; /* no text */
1375 outflags
&= ~MD_spc
;
1377 md_word(descr
->string
);
1378 descr
= descr
->next
;
1379 } while (descr
!= punct
);
1380 outflags
&= ~MD_spc
;
1384 md_uri(link
->string
);
1385 outflags
&= ~MD_spc
;
1388 /* Trailing punctuation. */
1389 while (punct
!= NULL
) {
1390 md_word(punct
->string
);
1391 punct
= punct
->next
;
1397 md_pre_Mt(struct roff_node
*n
)
1399 const struct roff_node
*nch
;
1402 outflags
&= ~MD_spc
;
1403 for (nch
= n
->child
; nch
!= NULL
; nch
= nch
->next
)
1404 md_word(nch
->string
);
1405 outflags
&= ~MD_spc
;
1406 md_rawword("](mailto:");
1407 for (nch
= n
->child
; nch
!= NULL
; nch
= nch
->next
) {
1408 md_uri(nch
->string
);
1409 if (nch
->next
!= NULL
) {
1414 outflags
&= ~MD_spc
;
1420 md_pre_Nd(struct roff_node
*n
)
1429 md_pre_Nm(struct roff_node
*n
)
1447 md_post_Nm(struct roff_node
*n
)
1463 md_pre_No(struct roff_node
*n
)
1465 outflags
|= MD_spc_force
;
1470 md_pre_Ns(struct roff_node
*n
)
1472 outflags
&= ~MD_spc
;
1477 md_post_Pf(struct roff_node
*n
)
1479 if (n
->next
!= NULL
&& (n
->next
->flags
& NODE_LINE
) == 0)
1480 outflags
&= ~MD_spc
;
1484 md_pre_Pp(struct roff_node
*n
)
1491 md_pre_Rs(struct roff_node
*n
)
1493 if (n
->sec
== SEC_SEE_ALSO
)
1499 md_pre_Sh(struct roff_node
*n
)
1503 if (n
->sec
== SEC_AUTHORS
)
1504 outflags
&= ~(MD_An_split
| MD_An_nosplit
);
1508 md_rawword(n
->tok
== MDOC_Sh
? "#" : "##");
1520 md_pre_Sm(struct roff_node
*n
)
1522 if (n
->child
== NULL
)
1524 else if (strcmp("on", n
->child
->string
) == 0)
1529 if (outflags
& MD_Sm
)
1536 md_pre_Vt(struct roff_node
*n
)
1552 md_post_Vt(struct roff_node
*n
)
1565 md_pre_Xr(struct roff_node
*n
)
1574 outflags
&= ~MD_spc
;
1582 md_pre__T(struct roff_node
*n
)
1584 if (n
->parent
->tok
== MDOC_Rs
&& n
->parent
->norm
->Rs
.quote_T
)
1588 outflags
&= ~MD_spc
;
1593 md_post__T(struct roff_node
*n
)
1595 outflags
&= ~MD_spc
;
1596 if (n
->parent
->tok
== MDOC_Rs
&& n
->parent
->norm
->Rs
.quote_T
)
1604 md_pre_br(struct roff_node
*n
)