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