]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_markdown.c
in -man -Thtml, vertical spacing is required before .IP
[mandoc.git] / mdoc_markdown.c
1 /* $Id: mdoc_markdown.c,v 1.27 2018/10/25 01:32:40 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 <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 *md_act(enum roff_tok);
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
254 static const struct md_act *
255 md_act(enum roff_tok tok)
256 {
257 assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
258 return md_acts + (tok - MDOC_Dd);
259 }
260
261 void
262 markdown_mdoc(void *arg, const struct roff_man *mdoc)
263 {
264 outflags = MD_Sm;
265 md_word(mdoc->meta.title);
266 if (mdoc->meta.msec != NULL) {
267 outflags &= ~MD_spc;
268 md_word("(");
269 md_word(mdoc->meta.msec);
270 md_word(")");
271 }
272 md_word("-");
273 md_word(mdoc->meta.vol);
274 if (mdoc->meta.arch != NULL) {
275 md_word("(");
276 md_word(mdoc->meta.arch);
277 md_word(")");
278 }
279 outflags |= MD_sp;
280
281 md_nodelist(mdoc->first->child);
282
283 outflags |= MD_sp;
284 md_word(mdoc->meta.os);
285 md_word("-");
286 md_word(mdoc->meta.date);
287 putchar('\n');
288 }
289
290 static void
291 md_nodelist(struct roff_node *n)
292 {
293 while (n != NULL) {
294 md_node(n);
295 n = n->next;
296 }
297 }
298
299 static void
300 md_node(struct roff_node *n)
301 {
302 const struct md_act *act;
303 int cond, process_children;
304
305 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
306 return;
307
308 if (outflags & MD_nonl)
309 outflags &= ~(MD_nl | MD_sp);
310 else if (outflags & MD_spc && n->flags & NODE_LINE)
311 outflags |= MD_nl;
312
313 act = NULL;
314 cond = 0;
315 process_children = 1;
316 n->flags &= ~NODE_ENDED;
317
318 if (n->type == ROFFT_TEXT) {
319 if (n->flags & NODE_DELIMC)
320 outflags &= ~(MD_spc | MD_spc_force);
321 else if (outflags & MD_Sm)
322 outflags |= MD_spc_force;
323 md_word(n->string);
324 if (n->flags & NODE_DELIMO)
325 outflags &= ~(MD_spc | MD_spc_force);
326 else if (outflags & MD_Sm)
327 outflags |= MD_spc;
328 } else if (n->tok < ROFF_MAX) {
329 switch (n->tok) {
330 case ROFF_br:
331 process_children = md_pre_br(n);
332 break;
333 case ROFF_sp:
334 process_children = md_pre_Pp(n);
335 break;
336 default:
337 process_children = 0;
338 break;
339 }
340 } else {
341 act = md_act(n->tok);
342 cond = act->cond == NULL || (*act->cond)(n);
343 if (cond && act->pre != NULL &&
344 (n->end == ENDBODY_NOT || n->child != NULL))
345 process_children = (*act->pre)(n);
346 }
347
348 if (process_children && n->child != NULL)
349 md_nodelist(n->child);
350
351 if (n->flags & NODE_ENDED)
352 return;
353
354 if (cond && act->post != NULL)
355 (*act->post)(n);
356
357 if (n->end != ENDBODY_NOT)
358 n->body->flags |= NODE_ENDED;
359 }
360
361 static const char *
362 md_stack(char c)
363 {
364 static char *stack;
365 static size_t sz;
366 static size_t cur;
367
368 switch (c) {
369 case '\0':
370 break;
371 case (char)-1:
372 assert(cur);
373 stack[--cur] = '\0';
374 break;
375 default:
376 if (cur + 1 >= sz) {
377 sz += 8;
378 stack = mandoc_realloc(stack, sz);
379 }
380 stack[cur] = c;
381 stack[++cur] = '\0';
382 break;
383 }
384 return stack == NULL ? "" : stack;
385 }
386
387 /*
388 * Handle vertical and horizontal spacing.
389 */
390 static void
391 md_preword(void)
392 {
393 const char *cp;
394
395 /*
396 * If a list block is nested inside a code block or a blockquote,
397 * blank lines for paragraph breaks no longer work; instead,
398 * they terminate the list. Work around this markdown issue
399 * by using mere line breaks instead.
400 */
401
402 if (list_blocks && outflags & MD_sp) {
403 outflags &= ~MD_sp;
404 outflags |= MD_br;
405 }
406
407 /*
408 * End the old line if requested.
409 * Escape whitespace at the end of the markdown line
410 * such that it won't look like an output line break.
411 */
412
413 if (outflags & MD_sp)
414 putchar('\n');
415 else if (outflags & MD_br) {
416 putchar(' ');
417 putchar(' ');
418 } else if (outflags & MD_nl && escflags & ESC_EOL)
419 md_named("zwnj");
420
421 /* Start a new line if necessary. */
422
423 if (outflags & (MD_nl | MD_br | MD_sp)) {
424 putchar('\n');
425 for (cp = md_stack('\0'); *cp != '\0'; cp++) {
426 putchar(*cp);
427 if (*cp == '>')
428 putchar(' ');
429 }
430 outflags &= ~(MD_nl | MD_br | MD_sp);
431 escflags = ESC_BOL;
432 outcount = 0;
433
434 /* Handle horizontal spacing. */
435
436 } else if (outflags & MD_spc) {
437 if (outflags & MD_Bk)
438 fputs("&nbsp;", stdout);
439 else
440 putchar(' ');
441 escflags &= ~ESC_FON;
442 outcount++;
443 }
444
445 outflags &= ~(MD_spc_force | MD_nonl);
446 if (outflags & MD_Sm)
447 outflags |= MD_spc;
448 else
449 outflags &= ~MD_spc;
450 }
451
452 /*
453 * Print markdown syntax elements.
454 * Can also be used for constant strings when neither escaping
455 * nor delimiter handling is required.
456 */
457 static void
458 md_rawword(const char *s)
459 {
460 md_preword();
461
462 if (*s == '\0')
463 return;
464
465 if (escflags & ESC_FON) {
466 escflags &= ~ESC_FON;
467 if (*s == '*' && !code_blocks)
468 fputs("&zwnj;", stdout);
469 }
470
471 while (*s != '\0') {
472 switch(*s) {
473 case '*':
474 if (s[1] == '\0')
475 escflags |= ESC_FON;
476 break;
477 case '[':
478 escflags |= ESC_SQU;
479 break;
480 case ']':
481 escflags |= ESC_HYP;
482 escflags &= ~ESC_SQU;
483 break;
484 default:
485 break;
486 }
487 md_char(*s++);
488 }
489 if (s[-1] == ' ')
490 escflags |= ESC_EOL;
491 else
492 escflags &= ~ESC_EOL;
493 }
494
495 /*
496 * Print text and mdoc(7) syntax elements.
497 */
498 static void
499 md_word(const char *s)
500 {
501 const char *seq, *prevfont, *currfont, *nextfont;
502 char c;
503 int bs, sz, uc, breakline;
504
505 /* No spacing before closing delimiters. */
506 if (s[0] != '\0' && s[1] == '\0' &&
507 strchr("!),.:;?]", s[0]) != NULL &&
508 (outflags & MD_spc_force) == 0)
509 outflags &= ~MD_spc;
510
511 md_preword();
512
513 if (*s == '\0')
514 return;
515
516 /* No spacing after opening delimiters. */
517 if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
518 outflags &= ~MD_spc;
519
520 breakline = 0;
521 prevfont = currfont = "";
522 while ((c = *s++) != '\0') {
523 bs = 0;
524 switch(c) {
525 case ASCII_NBRSP:
526 if (code_blocks)
527 c = ' ';
528 else {
529 md_named("nbsp");
530 c = '\0';
531 }
532 break;
533 case ASCII_HYPH:
534 bs = escflags & ESC_BOL && !code_blocks;
535 c = '-';
536 break;
537 case ASCII_BREAK:
538 continue;
539 case '#':
540 case '+':
541 case '-':
542 bs = escflags & ESC_BOL && !code_blocks;
543 break;
544 case '(':
545 bs = escflags & ESC_HYP && !code_blocks;
546 break;
547 case ')':
548 bs = escflags & ESC_NUM && !code_blocks;
549 break;
550 case '*':
551 case '[':
552 case '_':
553 case '`':
554 bs = !code_blocks;
555 break;
556 case '.':
557 bs = escflags & ESC_NUM && !code_blocks;
558 break;
559 case '<':
560 if (code_blocks == 0) {
561 md_named("lt");
562 c = '\0';
563 }
564 break;
565 case '=':
566 if (escflags & ESC_BOL && !code_blocks) {
567 md_named("equals");
568 c = '\0';
569 }
570 break;
571 case '>':
572 if (code_blocks == 0) {
573 md_named("gt");
574 c = '\0';
575 }
576 break;
577 case '\\':
578 uc = 0;
579 nextfont = NULL;
580 switch (mandoc_escape(&s, &seq, &sz)) {
581 case ESCAPE_UNICODE:
582 uc = mchars_num2uc(seq + 1, sz - 1);
583 break;
584 case ESCAPE_NUMBERED:
585 uc = mchars_num2char(seq, sz);
586 break;
587 case ESCAPE_SPECIAL:
588 uc = mchars_spec2cp(seq, sz);
589 break;
590 case ESCAPE_DEVICE:
591 md_rawword("markdown");
592 continue;
593 case ESCAPE_FONTBOLD:
594 nextfont = "**";
595 break;
596 case ESCAPE_FONTITALIC:
597 nextfont = "*";
598 break;
599 case ESCAPE_FONTBI:
600 nextfont = "***";
601 break;
602 case ESCAPE_FONT:
603 case ESCAPE_FONTCW:
604 case ESCAPE_FONTROMAN:
605 nextfont = "";
606 break;
607 case ESCAPE_FONTPREV:
608 nextfont = prevfont;
609 break;
610 case ESCAPE_BREAK:
611 breakline = 1;
612 break;
613 case ESCAPE_NOSPACE:
614 case ESCAPE_SKIPCHAR:
615 case ESCAPE_OVERSTRIKE:
616 /* XXX not implemented */
617 /* FALLTHROUGH */
618 case ESCAPE_ERROR:
619 default:
620 break;
621 }
622 if (nextfont != NULL && !code_blocks) {
623 if (*currfont != '\0') {
624 outflags &= ~MD_spc;
625 md_rawword(currfont);
626 }
627 prevfont = currfont;
628 currfont = nextfont;
629 if (*currfont != '\0') {
630 outflags &= ~MD_spc;
631 md_rawword(currfont);
632 }
633 }
634 if (uc) {
635 if ((uc < 0x20 && uc != 0x09) ||
636 (uc > 0x7E && uc < 0xA0))
637 uc = 0xFFFD;
638 if (code_blocks) {
639 seq = mchars_uc2str(uc);
640 fputs(seq, stdout);
641 outcount += strlen(seq);
642 } else {
643 printf("&#%d;", uc);
644 outcount++;
645 }
646 escflags &= ~ESC_FON;
647 }
648 c = '\0';
649 break;
650 case ']':
651 bs = escflags & ESC_SQU && !code_blocks;
652 escflags |= ESC_HYP;
653 break;
654 default:
655 break;
656 }
657 if (bs)
658 putchar('\\');
659 md_char(c);
660 if (breakline &&
661 (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
662 printf(" \n");
663 breakline = 0;
664 while (*s == ' ' || *s == ASCII_NBRSP)
665 s++;
666 }
667 }
668 if (*currfont != '\0') {
669 outflags &= ~MD_spc;
670 md_rawword(currfont);
671 } else if (s[-2] == ' ')
672 escflags |= ESC_EOL;
673 else
674 escflags &= ~ESC_EOL;
675 }
676
677 /*
678 * Print a single HTML named character reference.
679 */
680 static void
681 md_named(const char *s)
682 {
683 printf("&%s;", s);
684 escflags &= ~(ESC_FON | ESC_EOL);
685 outcount++;
686 }
687
688 /*
689 * Print a single raw character and maintain certain escape flags.
690 */
691 static void
692 md_char(unsigned char c)
693 {
694 if (c != '\0') {
695 putchar(c);
696 if (c == '*')
697 escflags |= ESC_FON;
698 else
699 escflags &= ~ESC_FON;
700 outcount++;
701 }
702 if (c != ']')
703 escflags &= ~ESC_HYP;
704 if (c == ' ' || c == '\t' || c == '>')
705 return;
706 if (isdigit(c) == 0)
707 escflags &= ~ESC_NUM;
708 else if (escflags & ESC_BOL)
709 escflags |= ESC_NUM;
710 escflags &= ~ESC_BOL;
711 }
712
713 static int
714 md_cond_head(struct roff_node *n)
715 {
716 return n->type == ROFFT_HEAD;
717 }
718
719 static int
720 md_cond_body(struct roff_node *n)
721 {
722 return n->type == ROFFT_BODY;
723 }
724
725 static int
726 md_pre_raw(struct roff_node *n)
727 {
728 const char *prefix;
729
730 if ((prefix = md_act(n->tok)->prefix) != NULL) {
731 md_rawword(prefix);
732 outflags &= ~MD_spc;
733 if (*prefix == '`')
734 code_blocks++;
735 }
736 return 1;
737 }
738
739 static void
740 md_post_raw(struct roff_node *n)
741 {
742 const char *suffix;
743
744 if ((suffix = md_act(n->tok)->suffix) != NULL) {
745 outflags &= ~(MD_spc | MD_nl);
746 md_rawword(suffix);
747 if (*suffix == '`')
748 code_blocks--;
749 }
750 }
751
752 static int
753 md_pre_word(struct roff_node *n)
754 {
755 const char *prefix;
756
757 if ((prefix = md_act(n->tok)->prefix) != NULL) {
758 md_word(prefix);
759 outflags &= ~MD_spc;
760 }
761 return 1;
762 }
763
764 static void
765 md_post_word(struct roff_node *n)
766 {
767 const char *suffix;
768
769 if ((suffix = md_act(n->tok)->suffix) != NULL) {
770 outflags &= ~(MD_spc | MD_nl);
771 md_word(suffix);
772 }
773 }
774
775 static void
776 md_post_pc(struct roff_node *n)
777 {
778 md_post_raw(n);
779 if (n->parent->tok != MDOC_Rs)
780 return;
781 if (n->next != NULL) {
782 md_word(",");
783 if (n->prev != NULL &&
784 n->prev->tok == n->tok &&
785 n->next->tok == n->tok)
786 md_word("and");
787 } else {
788 md_word(".");
789 outflags |= MD_nl;
790 }
791 }
792
793 static int
794 md_pre_skip(struct roff_node *n)
795 {
796 return 0;
797 }
798
799 static void
800 md_pre_syn(struct roff_node *n)
801 {
802 if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
803 return;
804
805 if (n->prev->tok == n->tok &&
806 n->tok != MDOC_Ft &&
807 n->tok != MDOC_Fo &&
808 n->tok != MDOC_Fn) {
809 outflags |= MD_br;
810 return;
811 }
812
813 switch (n->prev->tok) {
814 case MDOC_Fd:
815 case MDOC_Fn:
816 case MDOC_Fo:
817 case MDOC_In:
818 case MDOC_Vt:
819 outflags |= MD_sp;
820 break;
821 case MDOC_Ft:
822 if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
823 outflags |= MD_sp;
824 break;
825 }
826 /* FALLTHROUGH */
827 default:
828 outflags |= MD_br;
829 break;
830 }
831 }
832
833 static int
834 md_pre_An(struct roff_node *n)
835 {
836 switch (n->norm->An.auth) {
837 case AUTH_split:
838 outflags &= ~MD_An_nosplit;
839 outflags |= MD_An_split;
840 return 0;
841 case AUTH_nosplit:
842 outflags &= ~MD_An_split;
843 outflags |= MD_An_nosplit;
844 return 0;
845 default:
846 if (outflags & MD_An_split)
847 outflags |= MD_br;
848 else if (n->sec == SEC_AUTHORS &&
849 ! (outflags & MD_An_nosplit))
850 outflags |= MD_An_split;
851 return 1;
852 }
853 }
854
855 static int
856 md_pre_Ap(struct roff_node *n)
857 {
858 outflags &= ~MD_spc;
859 md_word("'");
860 outflags &= ~MD_spc;
861 return 0;
862 }
863
864 static int
865 md_pre_Bd(struct roff_node *n)
866 {
867 switch (n->norm->Bd.type) {
868 case DISP_unfilled:
869 case DISP_literal:
870 return md_pre_Dl(n);
871 default:
872 return md_pre_D1(n);
873 }
874 }
875
876 static int
877 md_pre_Bk(struct roff_node *n)
878 {
879 switch (n->type) {
880 case ROFFT_BLOCK:
881 return 1;
882 case ROFFT_BODY:
883 outflags |= MD_Bk;
884 return 1;
885 default:
886 return 0;
887 }
888 }
889
890 static void
891 md_post_Bk(struct roff_node *n)
892 {
893 if (n->type == ROFFT_BODY)
894 outflags &= ~MD_Bk;
895 }
896
897 static int
898 md_pre_Bl(struct roff_node *n)
899 {
900 n->norm->Bl.count = 0;
901 if (n->norm->Bl.type == LIST_column)
902 md_pre_Dl(n);
903 outflags |= MD_sp;
904 return 1;
905 }
906
907 static void
908 md_post_Bl(struct roff_node *n)
909 {
910 n->norm->Bl.count = 0;
911 if (n->norm->Bl.type == LIST_column)
912 md_post_D1(n);
913 outflags |= MD_sp;
914 }
915
916 static int
917 md_pre_D1(struct roff_node *n)
918 {
919 /*
920 * Markdown blockquote syntax does not work inside code blocks.
921 * The best we can do is fall back to another nested code block.
922 */
923 if (code_blocks) {
924 md_stack('\t');
925 code_blocks++;
926 } else {
927 md_stack('>');
928 quote_blocks++;
929 }
930 outflags |= MD_sp;
931 return 1;
932 }
933
934 static void
935 md_post_D1(struct roff_node *n)
936 {
937 md_stack((char)-1);
938 if (code_blocks)
939 code_blocks--;
940 else
941 quote_blocks--;
942 outflags |= MD_sp;
943 }
944
945 static int
946 md_pre_Dl(struct roff_node *n)
947 {
948 /*
949 * Markdown code block syntax does not work inside blockquotes.
950 * The best we can do is fall back to another nested blockquote.
951 */
952 if (quote_blocks) {
953 md_stack('>');
954 quote_blocks++;
955 } else {
956 md_stack('\t');
957 code_blocks++;
958 }
959 outflags |= MD_sp;
960 return 1;
961 }
962
963 static int
964 md_pre_En(struct roff_node *n)
965 {
966 if (n->norm->Es == NULL ||
967 n->norm->Es->child == NULL)
968 return 1;
969
970 md_word(n->norm->Es->child->string);
971 outflags &= ~MD_spc;
972 return 1;
973 }
974
975 static void
976 md_post_En(struct roff_node *n)
977 {
978 if (n->norm->Es == NULL ||
979 n->norm->Es->child == NULL ||
980 n->norm->Es->child->next == NULL)
981 return;
982
983 outflags &= ~MD_spc;
984 md_word(n->norm->Es->child->next->string);
985 }
986
987 static int
988 md_pre_Eo(struct roff_node *n)
989 {
990 if (n->end == ENDBODY_NOT &&
991 n->parent->head->child == NULL &&
992 n->child != NULL &&
993 n->child->end != ENDBODY_NOT)
994 md_preword();
995 else if (n->end != ENDBODY_NOT ? n->child != NULL :
996 n->parent->head->child != NULL && (n->child != NULL ||
997 (n->parent->tail != NULL && n->parent->tail->child != NULL)))
998 outflags &= ~(MD_spc | MD_nl);
999 return 1;
1000 }
1001
1002 static void
1003 md_post_Eo(struct roff_node *n)
1004 {
1005 if (n->end != ENDBODY_NOT) {
1006 outflags |= MD_spc;
1007 return;
1008 }
1009
1010 if (n->child == NULL && n->parent->head->child == NULL)
1011 return;
1012
1013 if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1014 outflags &= ~MD_spc;
1015 else
1016 outflags |= MD_spc;
1017 }
1018
1019 static int
1020 md_pre_Fa(struct roff_node *n)
1021 {
1022 int am_Fa;
1023
1024 am_Fa = n->tok == MDOC_Fa;
1025
1026 if (am_Fa)
1027 n = n->child;
1028
1029 while (n != NULL) {
1030 md_rawword("*");
1031 outflags &= ~MD_spc;
1032 md_node(n);
1033 outflags &= ~MD_spc;
1034 md_rawword("*");
1035 if ((n = n->next) != NULL)
1036 md_word(",");
1037 }
1038 return 0;
1039 }
1040
1041 static void
1042 md_post_Fa(struct roff_node *n)
1043 {
1044 if (n->next != NULL && n->next->tok == MDOC_Fa)
1045 md_word(",");
1046 }
1047
1048 static int
1049 md_pre_Fd(struct roff_node *n)
1050 {
1051 md_pre_syn(n);
1052 md_pre_raw(n);
1053 return 1;
1054 }
1055
1056 static void
1057 md_post_Fd(struct roff_node *n)
1058 {
1059 md_post_raw(n);
1060 outflags |= MD_br;
1061 }
1062
1063 static void
1064 md_post_Fl(struct roff_node *n)
1065 {
1066 md_post_raw(n);
1067 if (n->child == NULL && n->next != NULL &&
1068 n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1069 outflags &= ~MD_spc;
1070 }
1071
1072 static int
1073 md_pre_Fn(struct roff_node *n)
1074 {
1075 md_pre_syn(n);
1076
1077 if ((n = n->child) == NULL)
1078 return 0;
1079
1080 md_rawword("**");
1081 outflags &= ~MD_spc;
1082 md_node(n);
1083 outflags &= ~MD_spc;
1084 md_rawword("**");
1085 outflags &= ~MD_spc;
1086 md_word("(");
1087
1088 if ((n = n->next) != NULL)
1089 md_pre_Fa(n);
1090 return 0;
1091 }
1092
1093 static void
1094 md_post_Fn(struct roff_node *n)
1095 {
1096 md_word(")");
1097 if (n->flags & NODE_SYNPRETTY) {
1098 md_word(";");
1099 outflags |= MD_sp;
1100 }
1101 }
1102
1103 static int
1104 md_pre_Fo(struct roff_node *n)
1105 {
1106 switch (n->type) {
1107 case ROFFT_BLOCK:
1108 md_pre_syn(n);
1109 break;
1110 case ROFFT_HEAD:
1111 if (n->child == NULL)
1112 return 0;
1113 md_pre_raw(n);
1114 break;
1115 case ROFFT_BODY:
1116 outflags &= ~(MD_spc | MD_nl);
1117 md_word("(");
1118 break;
1119 default:
1120 break;
1121 }
1122 return 1;
1123 }
1124
1125 static void
1126 md_post_Fo(struct roff_node *n)
1127 {
1128 switch (n->type) {
1129 case ROFFT_HEAD:
1130 if (n->child != NULL)
1131 md_post_raw(n);
1132 break;
1133 case ROFFT_BODY:
1134 md_post_Fn(n);
1135 break;
1136 default:
1137 break;
1138 }
1139 }
1140
1141 static int
1142 md_pre_In(struct roff_node *n)
1143 {
1144 if (n->flags & NODE_SYNPRETTY) {
1145 md_pre_syn(n);
1146 md_rawword("**");
1147 outflags &= ~MD_spc;
1148 md_word("#include <");
1149 } else {
1150 md_word("<");
1151 outflags &= ~MD_spc;
1152 md_rawword("*");
1153 }
1154 outflags &= ~MD_spc;
1155 return 1;
1156 }
1157
1158 static void
1159 md_post_In(struct roff_node *n)
1160 {
1161 if (n->flags & NODE_SYNPRETTY) {
1162 outflags &= ~MD_spc;
1163 md_rawword(">**");
1164 outflags |= MD_nl;
1165 } else {
1166 outflags &= ~MD_spc;
1167 md_rawword("*>");
1168 }
1169 }
1170
1171 static int
1172 md_pre_It(struct roff_node *n)
1173 {
1174 struct roff_node *bln;
1175
1176 switch (n->type) {
1177 case ROFFT_BLOCK:
1178 return 1;
1179
1180 case ROFFT_HEAD:
1181 bln = n->parent->parent;
1182 if (bln->norm->Bl.comp == 0 &&
1183 bln->norm->Bl.type != LIST_column)
1184 outflags |= MD_sp;
1185 outflags |= MD_nl;
1186
1187 switch (bln->norm->Bl.type) {
1188 case LIST_item:
1189 outflags |= MD_br;
1190 return 0;
1191 case LIST_inset:
1192 case LIST_diag:
1193 case LIST_ohang:
1194 outflags |= MD_br;
1195 return 1;
1196 case LIST_tag:
1197 case LIST_hang:
1198 outflags |= MD_sp;
1199 return 1;
1200 case LIST_bullet:
1201 md_rawword("*\t");
1202 break;
1203 case LIST_dash:
1204 case LIST_hyphen:
1205 md_rawword("-\t");
1206 break;
1207 case LIST_enum:
1208 md_preword();
1209 if (bln->norm->Bl.count < 99)
1210 bln->norm->Bl.count++;
1211 printf("%d.\t", bln->norm->Bl.count);
1212 escflags &= ~ESC_FON;
1213 break;
1214 case LIST_column:
1215 outflags |= MD_br;
1216 return 0;
1217 default:
1218 return 0;
1219 }
1220 outflags &= ~MD_spc;
1221 outflags |= MD_nonl;
1222 outcount = 0;
1223 md_stack('\t');
1224 if (code_blocks || quote_blocks)
1225 list_blocks++;
1226 return 0;
1227
1228 case ROFFT_BODY:
1229 bln = n->parent->parent;
1230 switch (bln->norm->Bl.type) {
1231 case LIST_ohang:
1232 outflags |= MD_br;
1233 break;
1234 case LIST_tag:
1235 case LIST_hang:
1236 md_pre_D1(n);
1237 break;
1238 default:
1239 break;
1240 }
1241 return 1;
1242
1243 default:
1244 return 0;
1245 }
1246 }
1247
1248 static void
1249 md_post_It(struct roff_node *n)
1250 {
1251 struct roff_node *bln;
1252 int i, nc;
1253
1254 if (n->type != ROFFT_BODY)
1255 return;
1256
1257 bln = n->parent->parent;
1258 switch (bln->norm->Bl.type) {
1259 case LIST_bullet:
1260 case LIST_dash:
1261 case LIST_hyphen:
1262 case LIST_enum:
1263 md_stack((char)-1);
1264 if (code_blocks || quote_blocks)
1265 list_blocks--;
1266 break;
1267 case LIST_tag:
1268 case LIST_hang:
1269 md_post_D1(n);
1270 break;
1271
1272 case LIST_column:
1273 if (n->next == NULL)
1274 break;
1275
1276 /* Calculate the array index of the current column. */
1277
1278 i = 0;
1279 while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1280 i++;
1281
1282 /*
1283 * If a width was specified for this column,
1284 * subtract what printed, and
1285 * add the same spacing as in mdoc_term.c.
1286 */
1287
1288 nc = bln->norm->Bl.ncols;
1289 i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1290 (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1291 if (i < 1)
1292 i = 1;
1293 while (i-- > 0)
1294 putchar(' ');
1295
1296 outflags &= ~MD_spc;
1297 escflags &= ~ESC_FON;
1298 outcount = 0;
1299 break;
1300
1301 default:
1302 break;
1303 }
1304 }
1305
1306 static void
1307 md_post_Lb(struct roff_node *n)
1308 {
1309 if (n->sec == SEC_LIBRARY)
1310 outflags |= MD_br;
1311 }
1312
1313 static void
1314 md_uri(const char *s)
1315 {
1316 while (*s != '\0') {
1317 if (strchr("%()<>", *s) != NULL) {
1318 printf("%%%2.2hhX", *s);
1319 outcount += 3;
1320 } else {
1321 putchar(*s);
1322 outcount++;
1323 }
1324 s++;
1325 }
1326 }
1327
1328 static int
1329 md_pre_Lk(struct roff_node *n)
1330 {
1331 const struct roff_node *link, *descr, *punct;
1332
1333 if ((link = n->child) == NULL)
1334 return 0;
1335
1336 /* Find beginning of trailing punctuation. */
1337 punct = n->last;
1338 while (punct != link && punct->flags & NODE_DELIMC)
1339 punct = punct->prev;
1340 punct = punct->next;
1341
1342 /* Link text. */
1343 descr = link->next;
1344 if (descr == punct)
1345 descr = link; /* no text */
1346 md_rawword("[");
1347 outflags &= ~MD_spc;
1348 do {
1349 md_word(descr->string);
1350 descr = descr->next;
1351 } while (descr != punct);
1352 outflags &= ~MD_spc;
1353
1354 /* Link target. */
1355 md_rawword("](");
1356 md_uri(link->string);
1357 outflags &= ~MD_spc;
1358 md_rawword(")");
1359
1360 /* Trailing punctuation. */
1361 while (punct != NULL) {
1362 md_word(punct->string);
1363 punct = punct->next;
1364 }
1365 return 0;
1366 }
1367
1368 static int
1369 md_pre_Mt(struct roff_node *n)
1370 {
1371 const struct roff_node *nch;
1372
1373 md_rawword("[");
1374 outflags &= ~MD_spc;
1375 for (nch = n->child; nch != NULL; nch = nch->next)
1376 md_word(nch->string);
1377 outflags &= ~MD_spc;
1378 md_rawword("](mailto:");
1379 for (nch = n->child; nch != NULL; nch = nch->next) {
1380 md_uri(nch->string);
1381 if (nch->next != NULL) {
1382 putchar(' ');
1383 outcount++;
1384 }
1385 }
1386 outflags &= ~MD_spc;
1387 md_rawword(")");
1388 return 0;
1389 }
1390
1391 static int
1392 md_pre_Nd(struct roff_node *n)
1393 {
1394 outflags &= ~MD_nl;
1395 outflags |= MD_spc;
1396 md_word("-");
1397 return 1;
1398 }
1399
1400 static int
1401 md_pre_Nm(struct roff_node *n)
1402 {
1403 switch (n->type) {
1404 case ROFFT_BLOCK:
1405 outflags |= MD_Bk;
1406 md_pre_syn(n);
1407 break;
1408 case ROFFT_HEAD:
1409 case ROFFT_ELEM:
1410 md_pre_raw(n);
1411 break;
1412 default:
1413 break;
1414 }
1415 return 1;
1416 }
1417
1418 static void
1419 md_post_Nm(struct roff_node *n)
1420 {
1421 switch (n->type) {
1422 case ROFFT_BLOCK:
1423 outflags &= ~MD_Bk;
1424 break;
1425 case ROFFT_HEAD:
1426 case ROFFT_ELEM:
1427 md_post_raw(n);
1428 break;
1429 default:
1430 break;
1431 }
1432 }
1433
1434 static int
1435 md_pre_No(struct roff_node *n)
1436 {
1437 outflags |= MD_spc_force;
1438 return 1;
1439 }
1440
1441 static int
1442 md_pre_Ns(struct roff_node *n)
1443 {
1444 outflags &= ~MD_spc;
1445 return 0;
1446 }
1447
1448 static void
1449 md_post_Pf(struct roff_node *n)
1450 {
1451 if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1452 outflags &= ~MD_spc;
1453 }
1454
1455 static int
1456 md_pre_Pp(struct roff_node *n)
1457 {
1458 outflags |= MD_sp;
1459 return 0;
1460 }
1461
1462 static int
1463 md_pre_Rs(struct roff_node *n)
1464 {
1465 if (n->sec == SEC_SEE_ALSO)
1466 outflags |= MD_sp;
1467 return 1;
1468 }
1469
1470 static int
1471 md_pre_Sh(struct roff_node *n)
1472 {
1473 switch (n->type) {
1474 case ROFFT_BLOCK:
1475 if (n->sec == SEC_AUTHORS)
1476 outflags &= ~(MD_An_split | MD_An_nosplit);
1477 break;
1478 case ROFFT_HEAD:
1479 outflags |= MD_sp;
1480 md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1481 break;
1482 case ROFFT_BODY:
1483 outflags |= MD_sp;
1484 break;
1485 default:
1486 break;
1487 }
1488 return 1;
1489 }
1490
1491 static int
1492 md_pre_Sm(struct roff_node *n)
1493 {
1494 if (n->child == NULL)
1495 outflags ^= MD_Sm;
1496 else if (strcmp("on", n->child->string) == 0)
1497 outflags |= MD_Sm;
1498 else
1499 outflags &= ~MD_Sm;
1500
1501 if (outflags & MD_Sm)
1502 outflags |= MD_spc;
1503
1504 return 0;
1505 }
1506
1507 static int
1508 md_pre_Vt(struct roff_node *n)
1509 {
1510 switch (n->type) {
1511 case ROFFT_BLOCK:
1512 md_pre_syn(n);
1513 return 1;
1514 case ROFFT_BODY:
1515 case ROFFT_ELEM:
1516 md_pre_raw(n);
1517 return 1;
1518 default:
1519 return 0;
1520 }
1521 }
1522
1523 static void
1524 md_post_Vt(struct roff_node *n)
1525 {
1526 switch (n->type) {
1527 case ROFFT_BODY:
1528 case ROFFT_ELEM:
1529 md_post_raw(n);
1530 break;
1531 default:
1532 break;
1533 }
1534 }
1535
1536 static int
1537 md_pre_Xr(struct roff_node *n)
1538 {
1539 n = n->child;
1540 if (n == NULL)
1541 return 0;
1542 md_node(n);
1543 n = n->next;
1544 if (n == NULL)
1545 return 0;
1546 outflags &= ~MD_spc;
1547 md_word("(");
1548 md_node(n);
1549 md_word(")");
1550 return 0;
1551 }
1552
1553 static int
1554 md_pre__T(struct roff_node *n)
1555 {
1556 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1557 md_word("\"");
1558 else
1559 md_rawword("*");
1560 outflags &= ~MD_spc;
1561 return 1;
1562 }
1563
1564 static void
1565 md_post__T(struct roff_node *n)
1566 {
1567 outflags &= ~MD_spc;
1568 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1569 md_word("\"");
1570 else
1571 md_rawword("*");
1572 md_post_pc(n);
1573 }
1574
1575 static int
1576 md_pre_br(struct roff_node *n)
1577 {
1578 outflags |= MD_br;
1579 return 0;
1580 }