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