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