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