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