]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_markdown.c
add arch.o
[mandoc.git] / mdoc_markdown.c
1 /* $Id: mdoc_markdown.c,v 1.30 2018/12/30 00:49:55 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_meta *mdoc)
265 {
266 outflags = MD_Sm;
267 md_word(mdoc->title);
268 if (mdoc->msec != NULL) {
269 outflags &= ~MD_spc;
270 md_word("(");
271 md_word(mdoc->msec);
272 md_word(")");
273 }
274 md_word("-");
275 md_word(mdoc->vol);
276 if (mdoc->arch != NULL) {
277 md_word("(");
278 md_word(mdoc->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->os);
287 md_word("-");
288 md_word(mdoc->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_UNDEF:
593 uc = *seq;
594 break;
595 case ESCAPE_DEVICE:
596 md_rawword("markdown");
597 continue;
598 case ESCAPE_FONTBOLD:
599 nextfont = "**";
600 break;
601 case ESCAPE_FONTITALIC:
602 nextfont = "*";
603 break;
604 case ESCAPE_FONTBI:
605 nextfont = "***";
606 break;
607 case ESCAPE_FONT:
608 case ESCAPE_FONTCW:
609 case ESCAPE_FONTROMAN:
610 nextfont = "";
611 break;
612 case ESCAPE_FONTPREV:
613 nextfont = prevfont;
614 break;
615 case ESCAPE_BREAK:
616 breakline = 1;
617 break;
618 case ESCAPE_NOSPACE:
619 case ESCAPE_SKIPCHAR:
620 case ESCAPE_OVERSTRIKE:
621 /* XXX not implemented */
622 /* FALLTHROUGH */
623 case ESCAPE_ERROR:
624 default:
625 break;
626 }
627 if (nextfont != NULL && !code_blocks) {
628 if (*currfont != '\0') {
629 outflags &= ~MD_spc;
630 md_rawword(currfont);
631 }
632 prevfont = currfont;
633 currfont = nextfont;
634 if (*currfont != '\0') {
635 outflags &= ~MD_spc;
636 md_rawword(currfont);
637 }
638 }
639 if (uc) {
640 if ((uc < 0x20 && uc != 0x09) ||
641 (uc > 0x7E && uc < 0xA0))
642 uc = 0xFFFD;
643 if (code_blocks) {
644 seq = mchars_uc2str(uc);
645 fputs(seq, stdout);
646 outcount += strlen(seq);
647 } else {
648 printf("&#%d;", uc);
649 outcount++;
650 }
651 escflags &= ~ESC_FON;
652 }
653 c = '\0';
654 break;
655 case ']':
656 bs = escflags & ESC_SQU && !code_blocks;
657 escflags |= ESC_HYP;
658 break;
659 default:
660 break;
661 }
662 if (bs)
663 putchar('\\');
664 md_char(c);
665 if (breakline &&
666 (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
667 printf(" \n");
668 breakline = 0;
669 while (*s == ' ' || *s == ASCII_NBRSP)
670 s++;
671 }
672 }
673 if (*currfont != '\0') {
674 outflags &= ~MD_spc;
675 md_rawword(currfont);
676 } else if (s[-2] == ' ')
677 escflags |= ESC_EOL;
678 else
679 escflags &= ~ESC_EOL;
680 }
681
682 /*
683 * Print a single HTML named character reference.
684 */
685 static void
686 md_named(const char *s)
687 {
688 printf("&%s;", s);
689 escflags &= ~(ESC_FON | ESC_EOL);
690 outcount++;
691 }
692
693 /*
694 * Print a single raw character and maintain certain escape flags.
695 */
696 static void
697 md_char(unsigned char c)
698 {
699 if (c != '\0') {
700 putchar(c);
701 if (c == '*')
702 escflags |= ESC_FON;
703 else
704 escflags &= ~ESC_FON;
705 outcount++;
706 }
707 if (c != ']')
708 escflags &= ~ESC_HYP;
709 if (c == ' ' || c == '\t' || c == '>')
710 return;
711 if (isdigit(c) == 0)
712 escflags &= ~ESC_NUM;
713 else if (escflags & ESC_BOL)
714 escflags |= ESC_NUM;
715 escflags &= ~ESC_BOL;
716 }
717
718 static int
719 md_cond_head(struct roff_node *n)
720 {
721 return n->type == ROFFT_HEAD;
722 }
723
724 static int
725 md_cond_body(struct roff_node *n)
726 {
727 return n->type == ROFFT_BODY;
728 }
729
730 static int
731 md_pre_abort(struct roff_node *n)
732 {
733 abort();
734 }
735
736 static int
737 md_pre_raw(struct roff_node *n)
738 {
739 const char *prefix;
740
741 if ((prefix = md_act(n->tok)->prefix) != NULL) {
742 md_rawword(prefix);
743 outflags &= ~MD_spc;
744 if (*prefix == '`')
745 code_blocks++;
746 }
747 return 1;
748 }
749
750 static void
751 md_post_raw(struct roff_node *n)
752 {
753 const char *suffix;
754
755 if ((suffix = md_act(n->tok)->suffix) != NULL) {
756 outflags &= ~(MD_spc | MD_nl);
757 md_rawword(suffix);
758 if (*suffix == '`')
759 code_blocks--;
760 }
761 }
762
763 static int
764 md_pre_word(struct roff_node *n)
765 {
766 const char *prefix;
767
768 if ((prefix = md_act(n->tok)->prefix) != NULL) {
769 md_word(prefix);
770 outflags &= ~MD_spc;
771 }
772 return 1;
773 }
774
775 static void
776 md_post_word(struct roff_node *n)
777 {
778 const char *suffix;
779
780 if ((suffix = md_act(n->tok)->suffix) != NULL) {
781 outflags &= ~(MD_spc | MD_nl);
782 md_word(suffix);
783 }
784 }
785
786 static void
787 md_post_pc(struct roff_node *n)
788 {
789 md_post_raw(n);
790 if (n->parent->tok != MDOC_Rs)
791 return;
792 if (n->next != NULL) {
793 md_word(",");
794 if (n->prev != NULL &&
795 n->prev->tok == n->tok &&
796 n->next->tok == n->tok)
797 md_word("and");
798 } else {
799 md_word(".");
800 outflags |= MD_nl;
801 }
802 }
803
804 static int
805 md_pre_skip(struct roff_node *n)
806 {
807 return 0;
808 }
809
810 static void
811 md_pre_syn(struct roff_node *n)
812 {
813 if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
814 return;
815
816 if (n->prev->tok == n->tok &&
817 n->tok != MDOC_Ft &&
818 n->tok != MDOC_Fo &&
819 n->tok != MDOC_Fn) {
820 outflags |= MD_br;
821 return;
822 }
823
824 switch (n->prev->tok) {
825 case MDOC_Fd:
826 case MDOC_Fn:
827 case MDOC_Fo:
828 case MDOC_In:
829 case MDOC_Vt:
830 outflags |= MD_sp;
831 break;
832 case MDOC_Ft:
833 if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
834 outflags |= MD_sp;
835 break;
836 }
837 /* FALLTHROUGH */
838 default:
839 outflags |= MD_br;
840 break;
841 }
842 }
843
844 static int
845 md_pre_An(struct roff_node *n)
846 {
847 switch (n->norm->An.auth) {
848 case AUTH_split:
849 outflags &= ~MD_An_nosplit;
850 outflags |= MD_An_split;
851 return 0;
852 case AUTH_nosplit:
853 outflags &= ~MD_An_split;
854 outflags |= MD_An_nosplit;
855 return 0;
856 default:
857 if (outflags & MD_An_split)
858 outflags |= MD_br;
859 else if (n->sec == SEC_AUTHORS &&
860 ! (outflags & MD_An_nosplit))
861 outflags |= MD_An_split;
862 return 1;
863 }
864 }
865
866 static int
867 md_pre_Ap(struct roff_node *n)
868 {
869 outflags &= ~MD_spc;
870 md_word("'");
871 outflags &= ~MD_spc;
872 return 0;
873 }
874
875 static int
876 md_pre_Bd(struct roff_node *n)
877 {
878 switch (n->norm->Bd.type) {
879 case DISP_unfilled:
880 case DISP_literal:
881 return md_pre_Dl(n);
882 default:
883 return md_pre_D1(n);
884 }
885 }
886
887 static int
888 md_pre_Bk(struct roff_node *n)
889 {
890 switch (n->type) {
891 case ROFFT_BLOCK:
892 return 1;
893 case ROFFT_BODY:
894 outflags |= MD_Bk;
895 return 1;
896 default:
897 return 0;
898 }
899 }
900
901 static void
902 md_post_Bk(struct roff_node *n)
903 {
904 if (n->type == ROFFT_BODY)
905 outflags &= ~MD_Bk;
906 }
907
908 static int
909 md_pre_Bl(struct roff_node *n)
910 {
911 n->norm->Bl.count = 0;
912 if (n->norm->Bl.type == LIST_column)
913 md_pre_Dl(n);
914 outflags |= MD_sp;
915 return 1;
916 }
917
918 static void
919 md_post_Bl(struct roff_node *n)
920 {
921 n->norm->Bl.count = 0;
922 if (n->norm->Bl.type == LIST_column)
923 md_post_D1(n);
924 outflags |= MD_sp;
925 }
926
927 static int
928 md_pre_D1(struct roff_node *n)
929 {
930 /*
931 * Markdown blockquote syntax does not work inside code blocks.
932 * The best we can do is fall back to another nested code block.
933 */
934 if (code_blocks) {
935 md_stack('\t');
936 code_blocks++;
937 } else {
938 md_stack('>');
939 quote_blocks++;
940 }
941 outflags |= MD_sp;
942 return 1;
943 }
944
945 static void
946 md_post_D1(struct roff_node *n)
947 {
948 md_stack((char)-1);
949 if (code_blocks)
950 code_blocks--;
951 else
952 quote_blocks--;
953 outflags |= MD_sp;
954 }
955
956 static int
957 md_pre_Dl(struct roff_node *n)
958 {
959 /*
960 * Markdown code block syntax does not work inside blockquotes.
961 * The best we can do is fall back to another nested blockquote.
962 */
963 if (quote_blocks) {
964 md_stack('>');
965 quote_blocks++;
966 } else {
967 md_stack('\t');
968 code_blocks++;
969 }
970 outflags |= MD_sp;
971 return 1;
972 }
973
974 static int
975 md_pre_En(struct roff_node *n)
976 {
977 if (n->norm->Es == NULL ||
978 n->norm->Es->child == NULL)
979 return 1;
980
981 md_word(n->norm->Es->child->string);
982 outflags &= ~MD_spc;
983 return 1;
984 }
985
986 static void
987 md_post_En(struct roff_node *n)
988 {
989 if (n->norm->Es == NULL ||
990 n->norm->Es->child == NULL ||
991 n->norm->Es->child->next == NULL)
992 return;
993
994 outflags &= ~MD_spc;
995 md_word(n->norm->Es->child->next->string);
996 }
997
998 static int
999 md_pre_Eo(struct roff_node *n)
1000 {
1001 if (n->end == ENDBODY_NOT &&
1002 n->parent->head->child == NULL &&
1003 n->child != NULL &&
1004 n->child->end != ENDBODY_NOT)
1005 md_preword();
1006 else if (n->end != ENDBODY_NOT ? n->child != NULL :
1007 n->parent->head->child != NULL && (n->child != NULL ||
1008 (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1009 outflags &= ~(MD_spc | MD_nl);
1010 return 1;
1011 }
1012
1013 static void
1014 md_post_Eo(struct roff_node *n)
1015 {
1016 if (n->end != ENDBODY_NOT) {
1017 outflags |= MD_spc;
1018 return;
1019 }
1020
1021 if (n->child == NULL && n->parent->head->child == NULL)
1022 return;
1023
1024 if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1025 outflags &= ~MD_spc;
1026 else
1027 outflags |= MD_spc;
1028 }
1029
1030 static int
1031 md_pre_Fa(struct roff_node *n)
1032 {
1033 int am_Fa;
1034
1035 am_Fa = n->tok == MDOC_Fa;
1036
1037 if (am_Fa)
1038 n = n->child;
1039
1040 while (n != NULL) {
1041 md_rawword("*");
1042 outflags &= ~MD_spc;
1043 md_node(n);
1044 outflags &= ~MD_spc;
1045 md_rawword("*");
1046 if ((n = n->next) != NULL)
1047 md_word(",");
1048 }
1049 return 0;
1050 }
1051
1052 static void
1053 md_post_Fa(struct roff_node *n)
1054 {
1055 if (n->next != NULL && n->next->tok == MDOC_Fa)
1056 md_word(",");
1057 }
1058
1059 static int
1060 md_pre_Fd(struct roff_node *n)
1061 {
1062 md_pre_syn(n);
1063 md_pre_raw(n);
1064 return 1;
1065 }
1066
1067 static void
1068 md_post_Fd(struct roff_node *n)
1069 {
1070 md_post_raw(n);
1071 outflags |= MD_br;
1072 }
1073
1074 static void
1075 md_post_Fl(struct roff_node *n)
1076 {
1077 md_post_raw(n);
1078 if (n->child == NULL && n->next != NULL &&
1079 n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1080 outflags &= ~MD_spc;
1081 }
1082
1083 static int
1084 md_pre_Fn(struct roff_node *n)
1085 {
1086 md_pre_syn(n);
1087
1088 if ((n = n->child) == NULL)
1089 return 0;
1090
1091 md_rawword("**");
1092 outflags &= ~MD_spc;
1093 md_node(n);
1094 outflags &= ~MD_spc;
1095 md_rawword("**");
1096 outflags &= ~MD_spc;
1097 md_word("(");
1098
1099 if ((n = n->next) != NULL)
1100 md_pre_Fa(n);
1101 return 0;
1102 }
1103
1104 static void
1105 md_post_Fn(struct roff_node *n)
1106 {
1107 md_word(")");
1108 if (n->flags & NODE_SYNPRETTY) {
1109 md_word(";");
1110 outflags |= MD_sp;
1111 }
1112 }
1113
1114 static int
1115 md_pre_Fo(struct roff_node *n)
1116 {
1117 switch (n->type) {
1118 case ROFFT_BLOCK:
1119 md_pre_syn(n);
1120 break;
1121 case ROFFT_HEAD:
1122 if (n->child == NULL)
1123 return 0;
1124 md_pre_raw(n);
1125 break;
1126 case ROFFT_BODY:
1127 outflags &= ~(MD_spc | MD_nl);
1128 md_word("(");
1129 break;
1130 default:
1131 break;
1132 }
1133 return 1;
1134 }
1135
1136 static void
1137 md_post_Fo(struct roff_node *n)
1138 {
1139 switch (n->type) {
1140 case ROFFT_HEAD:
1141 if (n->child != NULL)
1142 md_post_raw(n);
1143 break;
1144 case ROFFT_BODY:
1145 md_post_Fn(n);
1146 break;
1147 default:
1148 break;
1149 }
1150 }
1151
1152 static int
1153 md_pre_In(struct roff_node *n)
1154 {
1155 if (n->flags & NODE_SYNPRETTY) {
1156 md_pre_syn(n);
1157 md_rawword("**");
1158 outflags &= ~MD_spc;
1159 md_word("#include <");
1160 } else {
1161 md_word("<");
1162 outflags &= ~MD_spc;
1163 md_rawword("*");
1164 }
1165 outflags &= ~MD_spc;
1166 return 1;
1167 }
1168
1169 static void
1170 md_post_In(struct roff_node *n)
1171 {
1172 if (n->flags & NODE_SYNPRETTY) {
1173 outflags &= ~MD_spc;
1174 md_rawword(">**");
1175 outflags |= MD_nl;
1176 } else {
1177 outflags &= ~MD_spc;
1178 md_rawword("*>");
1179 }
1180 }
1181
1182 static int
1183 md_pre_It(struct roff_node *n)
1184 {
1185 struct roff_node *bln;
1186
1187 switch (n->type) {
1188 case ROFFT_BLOCK:
1189 return 1;
1190
1191 case ROFFT_HEAD:
1192 bln = n->parent->parent;
1193 if (bln->norm->Bl.comp == 0 &&
1194 bln->norm->Bl.type != LIST_column)
1195 outflags |= MD_sp;
1196 outflags |= MD_nl;
1197
1198 switch (bln->norm->Bl.type) {
1199 case LIST_item:
1200 outflags |= MD_br;
1201 return 0;
1202 case LIST_inset:
1203 case LIST_diag:
1204 case LIST_ohang:
1205 outflags |= MD_br;
1206 return 1;
1207 case LIST_tag:
1208 case LIST_hang:
1209 outflags |= MD_sp;
1210 return 1;
1211 case LIST_bullet:
1212 md_rawword("*\t");
1213 break;
1214 case LIST_dash:
1215 case LIST_hyphen:
1216 md_rawword("-\t");
1217 break;
1218 case LIST_enum:
1219 md_preword();
1220 if (bln->norm->Bl.count < 99)
1221 bln->norm->Bl.count++;
1222 printf("%d.\t", bln->norm->Bl.count);
1223 escflags &= ~ESC_FON;
1224 break;
1225 case LIST_column:
1226 outflags |= MD_br;
1227 return 0;
1228 default:
1229 return 0;
1230 }
1231 outflags &= ~MD_spc;
1232 outflags |= MD_nonl;
1233 outcount = 0;
1234 md_stack('\t');
1235 if (code_blocks || quote_blocks)
1236 list_blocks++;
1237 return 0;
1238
1239 case ROFFT_BODY:
1240 bln = n->parent->parent;
1241 switch (bln->norm->Bl.type) {
1242 case LIST_ohang:
1243 outflags |= MD_br;
1244 break;
1245 case LIST_tag:
1246 case LIST_hang:
1247 md_pre_D1(n);
1248 break;
1249 default:
1250 break;
1251 }
1252 return 1;
1253
1254 default:
1255 return 0;
1256 }
1257 }
1258
1259 static void
1260 md_post_It(struct roff_node *n)
1261 {
1262 struct roff_node *bln;
1263 int i, nc;
1264
1265 if (n->type != ROFFT_BODY)
1266 return;
1267
1268 bln = n->parent->parent;
1269 switch (bln->norm->Bl.type) {
1270 case LIST_bullet:
1271 case LIST_dash:
1272 case LIST_hyphen:
1273 case LIST_enum:
1274 md_stack((char)-1);
1275 if (code_blocks || quote_blocks)
1276 list_blocks--;
1277 break;
1278 case LIST_tag:
1279 case LIST_hang:
1280 md_post_D1(n);
1281 break;
1282
1283 case LIST_column:
1284 if (n->next == NULL)
1285 break;
1286
1287 /* Calculate the array index of the current column. */
1288
1289 i = 0;
1290 while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1291 i++;
1292
1293 /*
1294 * If a width was specified for this column,
1295 * subtract what printed, and
1296 * add the same spacing as in mdoc_term.c.
1297 */
1298
1299 nc = bln->norm->Bl.ncols;
1300 i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1301 (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1302 if (i < 1)
1303 i = 1;
1304 while (i-- > 0)
1305 putchar(' ');
1306
1307 outflags &= ~MD_spc;
1308 escflags &= ~ESC_FON;
1309 outcount = 0;
1310 break;
1311
1312 default:
1313 break;
1314 }
1315 }
1316
1317 static void
1318 md_post_Lb(struct roff_node *n)
1319 {
1320 if (n->sec == SEC_LIBRARY)
1321 outflags |= MD_br;
1322 }
1323
1324 static void
1325 md_uri(const char *s)
1326 {
1327 while (*s != '\0') {
1328 if (strchr("%()<>", *s) != NULL) {
1329 printf("%%%2.2hhX", *s);
1330 outcount += 3;
1331 } else {
1332 putchar(*s);
1333 outcount++;
1334 }
1335 s++;
1336 }
1337 }
1338
1339 static int
1340 md_pre_Lk(struct roff_node *n)
1341 {
1342 const struct roff_node *link, *descr, *punct;
1343
1344 if ((link = n->child) == NULL)
1345 return 0;
1346
1347 /* Find beginning of trailing punctuation. */
1348 punct = n->last;
1349 while (punct != link && punct->flags & NODE_DELIMC)
1350 punct = punct->prev;
1351 punct = punct->next;
1352
1353 /* Link text. */
1354 descr = link->next;
1355 if (descr == punct)
1356 descr = link; /* no text */
1357 md_rawword("[");
1358 outflags &= ~MD_spc;
1359 do {
1360 md_word(descr->string);
1361 descr = descr->next;
1362 } while (descr != punct);
1363 outflags &= ~MD_spc;
1364
1365 /* Link target. */
1366 md_rawword("](");
1367 md_uri(link->string);
1368 outflags &= ~MD_spc;
1369 md_rawword(")");
1370
1371 /* Trailing punctuation. */
1372 while (punct != NULL) {
1373 md_word(punct->string);
1374 punct = punct->next;
1375 }
1376 return 0;
1377 }
1378
1379 static int
1380 md_pre_Mt(struct roff_node *n)
1381 {
1382 const struct roff_node *nch;
1383
1384 md_rawword("[");
1385 outflags &= ~MD_spc;
1386 for (nch = n->child; nch != NULL; nch = nch->next)
1387 md_word(nch->string);
1388 outflags &= ~MD_spc;
1389 md_rawword("](mailto:");
1390 for (nch = n->child; nch != NULL; nch = nch->next) {
1391 md_uri(nch->string);
1392 if (nch->next != NULL) {
1393 putchar(' ');
1394 outcount++;
1395 }
1396 }
1397 outflags &= ~MD_spc;
1398 md_rawword(")");
1399 return 0;
1400 }
1401
1402 static int
1403 md_pre_Nd(struct roff_node *n)
1404 {
1405 outflags &= ~MD_nl;
1406 outflags |= MD_spc;
1407 md_word("-");
1408 return 1;
1409 }
1410
1411 static int
1412 md_pre_Nm(struct roff_node *n)
1413 {
1414 switch (n->type) {
1415 case ROFFT_BLOCK:
1416 outflags |= MD_Bk;
1417 md_pre_syn(n);
1418 break;
1419 case ROFFT_HEAD:
1420 case ROFFT_ELEM:
1421 md_pre_raw(n);
1422 break;
1423 default:
1424 break;
1425 }
1426 return 1;
1427 }
1428
1429 static void
1430 md_post_Nm(struct roff_node *n)
1431 {
1432 switch (n->type) {
1433 case ROFFT_BLOCK:
1434 outflags &= ~MD_Bk;
1435 break;
1436 case ROFFT_HEAD:
1437 case ROFFT_ELEM:
1438 md_post_raw(n);
1439 break;
1440 default:
1441 break;
1442 }
1443 }
1444
1445 static int
1446 md_pre_No(struct roff_node *n)
1447 {
1448 outflags |= MD_spc_force;
1449 return 1;
1450 }
1451
1452 static int
1453 md_pre_Ns(struct roff_node *n)
1454 {
1455 outflags &= ~MD_spc;
1456 return 0;
1457 }
1458
1459 static void
1460 md_post_Pf(struct roff_node *n)
1461 {
1462 if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1463 outflags &= ~MD_spc;
1464 }
1465
1466 static int
1467 md_pre_Pp(struct roff_node *n)
1468 {
1469 outflags |= MD_sp;
1470 return 0;
1471 }
1472
1473 static int
1474 md_pre_Rs(struct roff_node *n)
1475 {
1476 if (n->sec == SEC_SEE_ALSO)
1477 outflags |= MD_sp;
1478 return 1;
1479 }
1480
1481 static int
1482 md_pre_Sh(struct roff_node *n)
1483 {
1484 switch (n->type) {
1485 case ROFFT_BLOCK:
1486 if (n->sec == SEC_AUTHORS)
1487 outflags &= ~(MD_An_split | MD_An_nosplit);
1488 break;
1489 case ROFFT_HEAD:
1490 outflags |= MD_sp;
1491 md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1492 break;
1493 case ROFFT_BODY:
1494 outflags |= MD_sp;
1495 break;
1496 default:
1497 break;
1498 }
1499 return 1;
1500 }
1501
1502 static int
1503 md_pre_Sm(struct roff_node *n)
1504 {
1505 if (n->child == NULL)
1506 outflags ^= MD_Sm;
1507 else if (strcmp("on", n->child->string) == 0)
1508 outflags |= MD_Sm;
1509 else
1510 outflags &= ~MD_Sm;
1511
1512 if (outflags & MD_Sm)
1513 outflags |= MD_spc;
1514
1515 return 0;
1516 }
1517
1518 static int
1519 md_pre_Vt(struct roff_node *n)
1520 {
1521 switch (n->type) {
1522 case ROFFT_BLOCK:
1523 md_pre_syn(n);
1524 return 1;
1525 case ROFFT_BODY:
1526 case ROFFT_ELEM:
1527 md_pre_raw(n);
1528 return 1;
1529 default:
1530 return 0;
1531 }
1532 }
1533
1534 static void
1535 md_post_Vt(struct roff_node *n)
1536 {
1537 switch (n->type) {
1538 case ROFFT_BODY:
1539 case ROFFT_ELEM:
1540 md_post_raw(n);
1541 break;
1542 default:
1543 break;
1544 }
1545 }
1546
1547 static int
1548 md_pre_Xr(struct roff_node *n)
1549 {
1550 n = n->child;
1551 if (n == NULL)
1552 return 0;
1553 md_node(n);
1554 n = n->next;
1555 if (n == NULL)
1556 return 0;
1557 outflags &= ~MD_spc;
1558 md_word("(");
1559 md_node(n);
1560 md_word(")");
1561 return 0;
1562 }
1563
1564 static int
1565 md_pre__T(struct roff_node *n)
1566 {
1567 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1568 md_word("\"");
1569 else
1570 md_rawword("*");
1571 outflags &= ~MD_spc;
1572 return 1;
1573 }
1574
1575 static void
1576 md_post__T(struct roff_node *n)
1577 {
1578 outflags &= ~MD_spc;
1579 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1580 md_word("\"");
1581 else
1582 md_rawword("*");
1583 md_post_pc(n);
1584 }
1585
1586 static int
1587 md_pre_br(struct roff_node *n)
1588 {
1589 outflags |= MD_br;
1590 return 0;
1591 }