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