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