1*fc3ee6fdSchristos /* Id: mdoc_markdown.c,v 1.30 2018/12/30 00:49:55 schwarze Exp */
29d9b81f7Schristos /*
3*fc3ee6fdSchristos * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
49d9b81f7Schristos *
59d9b81f7Schristos * Permission to use, copy, modify, and distribute this software for any
69d9b81f7Schristos * purpose with or without fee is hereby granted, provided that the above
79d9b81f7Schristos * copyright notice and this permission notice appear in all copies.
89d9b81f7Schristos *
99d9b81f7Schristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
109d9b81f7Schristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119d9b81f7Schristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
129d9b81f7Schristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139d9b81f7Schristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149d9b81f7Schristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159d9b81f7Schristos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169d9b81f7Schristos */
179d9b81f7Schristos #include <sys/types.h>
189d9b81f7Schristos
199d9b81f7Schristos #include <assert.h>
209d9b81f7Schristos #include <ctype.h>
219d9b81f7Schristos #include <stdio.h>
22*fc3ee6fdSchristos #include <stdlib.h>
239d9b81f7Schristos #include <string.h>
249d9b81f7Schristos
259d9b81f7Schristos #include "mandoc_aux.h"
269d9b81f7Schristos #include "mandoc.h"
279d9b81f7Schristos #include "roff.h"
289d9b81f7Schristos #include "mdoc.h"
299d9b81f7Schristos #include "main.h"
309d9b81f7Schristos
319d9b81f7Schristos struct md_act {
329d9b81f7Schristos int (*cond)(struct roff_node *n);
339d9b81f7Schristos int (*pre)(struct roff_node *n);
349d9b81f7Schristos void (*post)(struct roff_node *n);
359d9b81f7Schristos const char *prefix; /* pre-node string constant */
369d9b81f7Schristos const char *suffix; /* post-node string constant */
379d9b81f7Schristos };
389d9b81f7Schristos
399d9b81f7Schristos static void md_nodelist(struct roff_node *);
409d9b81f7Schristos static void md_node(struct roff_node *);
419d9b81f7Schristos static const char *md_stack(char c);
429d9b81f7Schristos static void md_preword(void);
439d9b81f7Schristos static void md_rawword(const char *);
449d9b81f7Schristos static void md_word(const char *);
459d9b81f7Schristos static void md_named(const char *);
469d9b81f7Schristos static void md_char(unsigned char);
479d9b81f7Schristos static void md_uri(const char *);
489d9b81f7Schristos
499d9b81f7Schristos static int md_cond_head(struct roff_node *);
509d9b81f7Schristos static int md_cond_body(struct roff_node *);
519d9b81f7Schristos
52*fc3ee6fdSchristos static int md_pre_abort(struct roff_node *);
539d9b81f7Schristos static int md_pre_raw(struct roff_node *);
549d9b81f7Schristos static int md_pre_word(struct roff_node *);
559d9b81f7Schristos static int md_pre_skip(struct roff_node *);
569d9b81f7Schristos static void md_pre_syn(struct roff_node *);
579d9b81f7Schristos static int md_pre_An(struct roff_node *);
589d9b81f7Schristos static int md_pre_Ap(struct roff_node *);
599d9b81f7Schristos static int md_pre_Bd(struct roff_node *);
609d9b81f7Schristos static int md_pre_Bk(struct roff_node *);
619d9b81f7Schristos static int md_pre_Bl(struct roff_node *);
629d9b81f7Schristos static int md_pre_D1(struct roff_node *);
639d9b81f7Schristos static int md_pre_Dl(struct roff_node *);
649d9b81f7Schristos static int md_pre_En(struct roff_node *);
659d9b81f7Schristos static int md_pre_Eo(struct roff_node *);
669d9b81f7Schristos static int md_pre_Fa(struct roff_node *);
679d9b81f7Schristos static int md_pre_Fd(struct roff_node *);
689d9b81f7Schristos static int md_pre_Fn(struct roff_node *);
699d9b81f7Schristos static int md_pre_Fo(struct roff_node *);
709d9b81f7Schristos static int md_pre_In(struct roff_node *);
719d9b81f7Schristos static int md_pre_It(struct roff_node *);
729d9b81f7Schristos static int md_pre_Lk(struct roff_node *);
739d9b81f7Schristos static int md_pre_Mt(struct roff_node *);
749d9b81f7Schristos static int md_pre_Nd(struct roff_node *);
759d9b81f7Schristos static int md_pre_Nm(struct roff_node *);
769d9b81f7Schristos static int md_pre_No(struct roff_node *);
779d9b81f7Schristos static int md_pre_Ns(struct roff_node *);
789d9b81f7Schristos static int md_pre_Pp(struct roff_node *);
799d9b81f7Schristos static int md_pre_Rs(struct roff_node *);
809d9b81f7Schristos static int md_pre_Sh(struct roff_node *);
819d9b81f7Schristos static int md_pre_Sm(struct roff_node *);
829d9b81f7Schristos static int md_pre_Vt(struct roff_node *);
839d9b81f7Schristos static int md_pre_Xr(struct roff_node *);
849d9b81f7Schristos static int md_pre__T(struct roff_node *);
859d9b81f7Schristos static int md_pre_br(struct roff_node *);
869d9b81f7Schristos
879d9b81f7Schristos static void md_post_raw(struct roff_node *);
889d9b81f7Schristos static void md_post_word(struct roff_node *);
899d9b81f7Schristos static void md_post_pc(struct roff_node *);
909d9b81f7Schristos static void md_post_Bk(struct roff_node *);
919d9b81f7Schristos static void md_post_Bl(struct roff_node *);
929d9b81f7Schristos static void md_post_D1(struct roff_node *);
939d9b81f7Schristos static void md_post_En(struct roff_node *);
949d9b81f7Schristos static void md_post_Eo(struct roff_node *);
959d9b81f7Schristos static void md_post_Fa(struct roff_node *);
969d9b81f7Schristos static void md_post_Fd(struct roff_node *);
979d9b81f7Schristos static void md_post_Fl(struct roff_node *);
989d9b81f7Schristos static void md_post_Fn(struct roff_node *);
999d9b81f7Schristos static void md_post_Fo(struct roff_node *);
1009d9b81f7Schristos static void md_post_In(struct roff_node *);
1019d9b81f7Schristos static void md_post_It(struct roff_node *);
1029d9b81f7Schristos static void md_post_Lb(struct roff_node *);
1039d9b81f7Schristos static void md_post_Nm(struct roff_node *);
1049d9b81f7Schristos static void md_post_Pf(struct roff_node *);
1059d9b81f7Schristos static void md_post_Vt(struct roff_node *);
1069d9b81f7Schristos static void md_post__T(struct roff_node *);
1079d9b81f7Schristos
108*fc3ee6fdSchristos static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = {
1099d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Dd */
1109d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Dt */
1119d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Os */
1129d9b81f7Schristos { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
1139d9b81f7Schristos { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
1149d9b81f7Schristos { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
1159d9b81f7Schristos { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
1169d9b81f7Schristos { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
1179d9b81f7Schristos { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
1189d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ed */
1199d9b81f7Schristos { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
1209d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* El */
1219d9b81f7Schristos { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
1229d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
1239d9b81f7Schristos { NULL, md_pre_An, NULL, NULL, NULL }, /* An */
1249d9b81f7Schristos { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
1259d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
1269d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
1279d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
1289d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
1299d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
1309d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
1319d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ex */
1329d9b81f7Schristos { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
1339d9b81f7Schristos { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
1349d9b81f7Schristos { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
1359d9b81f7Schristos { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
1369d9b81f7Schristos { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
1379d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
1389d9b81f7Schristos { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
1399d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
1409d9b81f7Schristos { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
1419d9b81f7Schristos { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
1429d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
143*fc3ee6fdSchristos { NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */
1449d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
1459d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Rv */
1469d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* St */
1479d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
1489d9b81f7Schristos { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
1499d9b81f7Schristos { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
1509d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
1519d9b81f7Schristos { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
1529d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
1539d9b81f7Schristos { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
1549d9b81f7Schristos { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
1559d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
1569d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
1579d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
1589d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
1599d9b81f7Schristos { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
1609d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
1619d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ac */
1629d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
1639d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
1649d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* At */
1659d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Bc */
1669d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
1679d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
1689d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
1699d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Bsx */
1709d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Bx */
1719d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Db */
1729d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Dc */
1739d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
1749d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
1759d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ec */
1769d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ef */
1779d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
1789d9b81f7Schristos { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
1799d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Fx */
1809d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
1819d9b81f7Schristos { NULL, md_pre_No, NULL, NULL, NULL }, /* No */
1829d9b81f7Schristos { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
1839d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Nx */
1849d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ox */
1859d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Pc */
1869d9b81f7Schristos { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
1879d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
1889d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
1899d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Qc */
1909d9b81f7Schristos { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
1919d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
1929d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
1939d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Re */
1949d9b81f7Schristos { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
1959d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Sc */
1969d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
1979d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
1989d9b81f7Schristos { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
1999d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
2009d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
2019d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
2029d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ux */
2039d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Xc */
2049d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Xo */
2059d9b81f7Schristos { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
2069d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Fc */
2079d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
2089d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Oc */
2099d9b81f7Schristos { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
2109d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ek */
2119d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Bt */
2129d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Hf */
2139d9b81f7Schristos { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
2149d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ud */
2159d9b81f7Schristos { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
216*fc3ee6fdSchristos { NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */
2179d9b81f7Schristos { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
2189d9b81f7Schristos { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
2199d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
2209d9b81f7Schristos { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
2219d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Brc */
2229d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
2239d9b81f7Schristos { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
2249d9b81f7Schristos { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
2259d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Dx */
2269d9b81f7Schristos { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
2279d9b81f7Schristos { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
2289d9b81f7Schristos { NULL, NULL, NULL, NULL, NULL }, /* Ta */
2299d9b81f7Schristos };
230*fc3ee6fdSchristos static const struct md_act *md_act(enum roff_tok);
2319d9b81f7Schristos
2329d9b81f7Schristos static int outflags;
2339d9b81f7Schristos #define MD_spc (1 << 0) /* Blank character before next word. */
2349d9b81f7Schristos #define MD_spc_force (1 << 1) /* Even before trailing punctuation. */
2359d9b81f7Schristos #define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */
2369d9b81f7Schristos #define MD_nl (1 << 3) /* Break markdown code line. */
2379d9b81f7Schristos #define MD_br (1 << 4) /* Insert an output line break. */
2389d9b81f7Schristos #define MD_sp (1 << 5) /* Insert a paragraph break. */
2399d9b81f7Schristos #define MD_Sm (1 << 6) /* Horizontal spacing mode. */
2409d9b81f7Schristos #define MD_Bk (1 << 7) /* Word keep mode. */
2419d9b81f7Schristos #define MD_An_split (1 << 8) /* Author mode is "split". */
2429d9b81f7Schristos #define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */
2439d9b81f7Schristos
2449d9b81f7Schristos static int escflags; /* Escape in generated markdown code: */
2459d9b81f7Schristos #define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */
2469d9b81f7Schristos #define ESC_NUM (1 << 1) /* "." after a leading number. */
2479d9b81f7Schristos #define ESC_HYP (1 << 2) /* "(" immediately after "]". */
2489d9b81f7Schristos #define ESC_SQU (1 << 4) /* "]" when "[" is open. */
2499d9b81f7Schristos #define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */
2509d9b81f7Schristos #define ESC_EOL (1 << 6) /* " " at the and of a line. */
2519d9b81f7Schristos
2529d9b81f7Schristos static int code_blocks, quote_blocks, list_blocks;
2539d9b81f7Schristos static int outcount;
2549d9b81f7Schristos
255*fc3ee6fdSchristos
256*fc3ee6fdSchristos static const struct md_act *
md_act(enum roff_tok tok)257*fc3ee6fdSchristos md_act(enum roff_tok tok)
258*fc3ee6fdSchristos {
259*fc3ee6fdSchristos assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
260*fc3ee6fdSchristos return md_acts + (tok - MDOC_Dd);
261*fc3ee6fdSchristos }
262*fc3ee6fdSchristos
2639d9b81f7Schristos void
markdown_mdoc(void * arg,const struct roff_meta * mdoc)264*fc3ee6fdSchristos markdown_mdoc(void *arg, const struct roff_meta *mdoc)
2659d9b81f7Schristos {
2669d9b81f7Schristos outflags = MD_Sm;
267*fc3ee6fdSchristos md_word(mdoc->title);
268*fc3ee6fdSchristos if (mdoc->msec != NULL) {
2699d9b81f7Schristos outflags &= ~MD_spc;
2709d9b81f7Schristos md_word("(");
271*fc3ee6fdSchristos md_word(mdoc->msec);
2729d9b81f7Schristos md_word(")");
2739d9b81f7Schristos }
2749d9b81f7Schristos md_word("-");
275*fc3ee6fdSchristos md_word(mdoc->vol);
276*fc3ee6fdSchristos if (mdoc->arch != NULL) {
2779d9b81f7Schristos md_word("(");
278*fc3ee6fdSchristos md_word(mdoc->arch);
2799d9b81f7Schristos md_word(")");
2809d9b81f7Schristos }
2819d9b81f7Schristos outflags |= MD_sp;
2829d9b81f7Schristos
2839d9b81f7Schristos md_nodelist(mdoc->first->child);
2849d9b81f7Schristos
2859d9b81f7Schristos outflags |= MD_sp;
286*fc3ee6fdSchristos md_word(mdoc->os);
2879d9b81f7Schristos md_word("-");
288*fc3ee6fdSchristos md_word(mdoc->date);
2899d9b81f7Schristos putchar('\n');
2909d9b81f7Schristos }
2919d9b81f7Schristos
2929d9b81f7Schristos static void
md_nodelist(struct roff_node * n)2939d9b81f7Schristos md_nodelist(struct roff_node *n)
2949d9b81f7Schristos {
2959d9b81f7Schristos while (n != NULL) {
2969d9b81f7Schristos md_node(n);
2979d9b81f7Schristos n = n->next;
2989d9b81f7Schristos }
2999d9b81f7Schristos }
3009d9b81f7Schristos
3019d9b81f7Schristos static void
md_node(struct roff_node * n)3029d9b81f7Schristos md_node(struct roff_node *n)
3039d9b81f7Schristos {
3049d9b81f7Schristos const struct md_act *act;
3059d9b81f7Schristos int cond, process_children;
3069d9b81f7Schristos
3079d9b81f7Schristos if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
3089d9b81f7Schristos return;
3099d9b81f7Schristos
3109d9b81f7Schristos if (outflags & MD_nonl)
3119d9b81f7Schristos outflags &= ~(MD_nl | MD_sp);
3129d9b81f7Schristos else if (outflags & MD_spc && n->flags & NODE_LINE)
3139d9b81f7Schristos outflags |= MD_nl;
3149d9b81f7Schristos
3159d9b81f7Schristos act = NULL;
3169d9b81f7Schristos cond = 0;
3179d9b81f7Schristos process_children = 1;
3189d9b81f7Schristos n->flags &= ~NODE_ENDED;
3199d9b81f7Schristos
3209d9b81f7Schristos if (n->type == ROFFT_TEXT) {
3219d9b81f7Schristos if (n->flags & NODE_DELIMC)
3229d9b81f7Schristos outflags &= ~(MD_spc | MD_spc_force);
3239d9b81f7Schristos else if (outflags & MD_Sm)
3249d9b81f7Schristos outflags |= MD_spc_force;
3259d9b81f7Schristos md_word(n->string);
3269d9b81f7Schristos if (n->flags & NODE_DELIMO)
3279d9b81f7Schristos outflags &= ~(MD_spc | MD_spc_force);
3289d9b81f7Schristos else if (outflags & MD_Sm)
3299d9b81f7Schristos outflags |= MD_spc;
3309d9b81f7Schristos } else if (n->tok < ROFF_MAX) {
3319d9b81f7Schristos switch (n->tok) {
3329d9b81f7Schristos case ROFF_br:
3339d9b81f7Schristos process_children = md_pre_br(n);
3349d9b81f7Schristos break;
3359d9b81f7Schristos case ROFF_sp:
3369d9b81f7Schristos process_children = md_pre_Pp(n);
3379d9b81f7Schristos break;
3389d9b81f7Schristos default:
3399d9b81f7Schristos process_children = 0;
3409d9b81f7Schristos break;
3419d9b81f7Schristos }
3429d9b81f7Schristos } else {
343*fc3ee6fdSchristos act = md_act(n->tok);
3449d9b81f7Schristos cond = act->cond == NULL || (*act->cond)(n);
3459d9b81f7Schristos if (cond && act->pre != NULL &&
3469d9b81f7Schristos (n->end == ENDBODY_NOT || n->child != NULL))
3479d9b81f7Schristos process_children = (*act->pre)(n);
3489d9b81f7Schristos }
3499d9b81f7Schristos
3509d9b81f7Schristos if (process_children && n->child != NULL)
3519d9b81f7Schristos md_nodelist(n->child);
3529d9b81f7Schristos
3539d9b81f7Schristos if (n->flags & NODE_ENDED)
3549d9b81f7Schristos return;
3559d9b81f7Schristos
3569d9b81f7Schristos if (cond && act->post != NULL)
3579d9b81f7Schristos (*act->post)(n);
3589d9b81f7Schristos
3599d9b81f7Schristos if (n->end != ENDBODY_NOT)
3609d9b81f7Schristos n->body->flags |= NODE_ENDED;
3619d9b81f7Schristos }
3629d9b81f7Schristos
3639d9b81f7Schristos static const char *
md_stack(char c)3649d9b81f7Schristos md_stack(char c)
3659d9b81f7Schristos {
3669d9b81f7Schristos static char *stack;
3679d9b81f7Schristos static size_t sz;
3689d9b81f7Schristos static size_t cur;
3699d9b81f7Schristos
3709d9b81f7Schristos switch (c) {
3719d9b81f7Schristos case '\0':
3729d9b81f7Schristos break;
3739d9b81f7Schristos case (char)-1:
3749d9b81f7Schristos assert(cur);
3759d9b81f7Schristos stack[--cur] = '\0';
3769d9b81f7Schristos break;
3779d9b81f7Schristos default:
3789d9b81f7Schristos if (cur + 1 >= sz) {
3799d9b81f7Schristos sz += 8;
3809d9b81f7Schristos stack = mandoc_realloc(stack, sz);
3819d9b81f7Schristos }
3829d9b81f7Schristos stack[cur] = c;
3839d9b81f7Schristos stack[++cur] = '\0';
3849d9b81f7Schristos break;
3859d9b81f7Schristos }
3869d9b81f7Schristos return stack == NULL ? "" : stack;
3879d9b81f7Schristos }
3889d9b81f7Schristos
3899d9b81f7Schristos /*
3909d9b81f7Schristos * Handle vertical and horizontal spacing.
3919d9b81f7Schristos */
3929d9b81f7Schristos static void
md_preword(void)3939d9b81f7Schristos md_preword(void)
3949d9b81f7Schristos {
3959d9b81f7Schristos const char *cp;
3969d9b81f7Schristos
3979d9b81f7Schristos /*
3989d9b81f7Schristos * If a list block is nested inside a code block or a blockquote,
3999d9b81f7Schristos * blank lines for paragraph breaks no longer work; instead,
4009d9b81f7Schristos * they terminate the list. Work around this markdown issue
4019d9b81f7Schristos * by using mere line breaks instead.
4029d9b81f7Schristos */
4039d9b81f7Schristos
4049d9b81f7Schristos if (list_blocks && outflags & MD_sp) {
4059d9b81f7Schristos outflags &= ~MD_sp;
4069d9b81f7Schristos outflags |= MD_br;
4079d9b81f7Schristos }
4089d9b81f7Schristos
4099d9b81f7Schristos /*
4109d9b81f7Schristos * End the old line if requested.
4119d9b81f7Schristos * Escape whitespace at the end of the markdown line
4129d9b81f7Schristos * such that it won't look like an output line break.
4139d9b81f7Schristos */
4149d9b81f7Schristos
4159d9b81f7Schristos if (outflags & MD_sp)
4169d9b81f7Schristos putchar('\n');
4179d9b81f7Schristos else if (outflags & MD_br) {
4189d9b81f7Schristos putchar(' ');
4199d9b81f7Schristos putchar(' ');
4209d9b81f7Schristos } else if (outflags & MD_nl && escflags & ESC_EOL)
4219d9b81f7Schristos md_named("zwnj");
4229d9b81f7Schristos
4239d9b81f7Schristos /* Start a new line if necessary. */
4249d9b81f7Schristos
4259d9b81f7Schristos if (outflags & (MD_nl | MD_br | MD_sp)) {
4269d9b81f7Schristos putchar('\n');
4279d9b81f7Schristos for (cp = md_stack('\0'); *cp != '\0'; cp++) {
4289d9b81f7Schristos putchar(*cp);
4299d9b81f7Schristos if (*cp == '>')
4309d9b81f7Schristos putchar(' ');
4319d9b81f7Schristos }
4329d9b81f7Schristos outflags &= ~(MD_nl | MD_br | MD_sp);
4339d9b81f7Schristos escflags = ESC_BOL;
4349d9b81f7Schristos outcount = 0;
4359d9b81f7Schristos
4369d9b81f7Schristos /* Handle horizontal spacing. */
4379d9b81f7Schristos
4389d9b81f7Schristos } else if (outflags & MD_spc) {
4399d9b81f7Schristos if (outflags & MD_Bk)
4409d9b81f7Schristos fputs(" ", stdout);
4419d9b81f7Schristos else
4429d9b81f7Schristos putchar(' ');
4439d9b81f7Schristos escflags &= ~ESC_FON;
4449d9b81f7Schristos outcount++;
4459d9b81f7Schristos }
4469d9b81f7Schristos
4479d9b81f7Schristos outflags &= ~(MD_spc_force | MD_nonl);
4489d9b81f7Schristos if (outflags & MD_Sm)
4499d9b81f7Schristos outflags |= MD_spc;
4509d9b81f7Schristos else
4519d9b81f7Schristos outflags &= ~MD_spc;
4529d9b81f7Schristos }
4539d9b81f7Schristos
4549d9b81f7Schristos /*
4559d9b81f7Schristos * Print markdown syntax elements.
4569d9b81f7Schristos * Can also be used for constant strings when neither escaping
4579d9b81f7Schristos * nor delimiter handling is required.
4589d9b81f7Schristos */
4599d9b81f7Schristos static void
md_rawword(const char * s)4609d9b81f7Schristos md_rawword(const char *s)
4619d9b81f7Schristos {
4629d9b81f7Schristos md_preword();
4639d9b81f7Schristos
4649d9b81f7Schristos if (*s == '\0')
4659d9b81f7Schristos return;
4669d9b81f7Schristos
4679d9b81f7Schristos if (escflags & ESC_FON) {
4689d9b81f7Schristos escflags &= ~ESC_FON;
4699d9b81f7Schristos if (*s == '*' && !code_blocks)
4709d9b81f7Schristos fputs("‌", stdout);
4719d9b81f7Schristos }
4729d9b81f7Schristos
4739d9b81f7Schristos while (*s != '\0') {
4749d9b81f7Schristos switch(*s) {
4759d9b81f7Schristos case '*':
4769d9b81f7Schristos if (s[1] == '\0')
4779d9b81f7Schristos escflags |= ESC_FON;
4789d9b81f7Schristos break;
4799d9b81f7Schristos case '[':
4809d9b81f7Schristos escflags |= ESC_SQU;
4819d9b81f7Schristos break;
4829d9b81f7Schristos case ']':
4839d9b81f7Schristos escflags |= ESC_HYP;
4849d9b81f7Schristos escflags &= ~ESC_SQU;
4859d9b81f7Schristos break;
4869d9b81f7Schristos default:
4879d9b81f7Schristos break;
4889d9b81f7Schristos }
4899d9b81f7Schristos md_char(*s++);
4909d9b81f7Schristos }
4919d9b81f7Schristos if (s[-1] == ' ')
4929d9b81f7Schristos escflags |= ESC_EOL;
4939d9b81f7Schristos else
4949d9b81f7Schristos escflags &= ~ESC_EOL;
4959d9b81f7Schristos }
4969d9b81f7Schristos
4979d9b81f7Schristos /*
4989d9b81f7Schristos * Print text and mdoc(7) syntax elements.
4999d9b81f7Schristos */
5009d9b81f7Schristos static void
md_word(const char * s)5019d9b81f7Schristos md_word(const char *s)
5029d9b81f7Schristos {
5039d9b81f7Schristos const char *seq, *prevfont, *currfont, *nextfont;
5049d9b81f7Schristos char c;
5059d9b81f7Schristos int bs, sz, uc, breakline;
5069d9b81f7Schristos
5079d9b81f7Schristos /* No spacing before closing delimiters. */
5089d9b81f7Schristos if (s[0] != '\0' && s[1] == '\0' &&
5099d9b81f7Schristos strchr("!),.:;?]", s[0]) != NULL &&
5109d9b81f7Schristos (outflags & MD_spc_force) == 0)
5119d9b81f7Schristos outflags &= ~MD_spc;
5129d9b81f7Schristos
5139d9b81f7Schristos md_preword();
5149d9b81f7Schristos
5159d9b81f7Schristos if (*s == '\0')
5169d9b81f7Schristos return;
5179d9b81f7Schristos
5189d9b81f7Schristos /* No spacing after opening delimiters. */
5199d9b81f7Schristos if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
5209d9b81f7Schristos outflags &= ~MD_spc;
5219d9b81f7Schristos
5229d9b81f7Schristos breakline = 0;
5239d9b81f7Schristos prevfont = currfont = "";
5249d9b81f7Schristos while ((c = *s++) != '\0') {
5259d9b81f7Schristos bs = 0;
5269d9b81f7Schristos switch(c) {
5279d9b81f7Schristos case ASCII_NBRSP:
5289d9b81f7Schristos if (code_blocks)
5299d9b81f7Schristos c = ' ';
5309d9b81f7Schristos else {
5319d9b81f7Schristos md_named("nbsp");
5329d9b81f7Schristos c = '\0';
5339d9b81f7Schristos }
5349d9b81f7Schristos break;
5359d9b81f7Schristos case ASCII_HYPH:
5369d9b81f7Schristos bs = escflags & ESC_BOL && !code_blocks;
5379d9b81f7Schristos c = '-';
5389d9b81f7Schristos break;
5399d9b81f7Schristos case ASCII_BREAK:
5409d9b81f7Schristos continue;
5419d9b81f7Schristos case '#':
5429d9b81f7Schristos case '+':
5439d9b81f7Schristos case '-':
5449d9b81f7Schristos bs = escflags & ESC_BOL && !code_blocks;
5459d9b81f7Schristos break;
5469d9b81f7Schristos case '(':
5479d9b81f7Schristos bs = escflags & ESC_HYP && !code_blocks;
5489d9b81f7Schristos break;
5499d9b81f7Schristos case ')':
5509d9b81f7Schristos bs = escflags & ESC_NUM && !code_blocks;
5519d9b81f7Schristos break;
5529d9b81f7Schristos case '*':
5539d9b81f7Schristos case '[':
5549d9b81f7Schristos case '_':
5559d9b81f7Schristos case '`':
5569d9b81f7Schristos bs = !code_blocks;
5579d9b81f7Schristos break;
5589d9b81f7Schristos case '.':
5599d9b81f7Schristos bs = escflags & ESC_NUM && !code_blocks;
5609d9b81f7Schristos break;
5619d9b81f7Schristos case '<':
5629d9b81f7Schristos if (code_blocks == 0) {
5639d9b81f7Schristos md_named("lt");
5649d9b81f7Schristos c = '\0';
5659d9b81f7Schristos }
5669d9b81f7Schristos break;
5679d9b81f7Schristos case '=':
5689d9b81f7Schristos if (escflags & ESC_BOL && !code_blocks) {
5699d9b81f7Schristos md_named("equals");
5709d9b81f7Schristos c = '\0';
5719d9b81f7Schristos }
5729d9b81f7Schristos break;
5739d9b81f7Schristos case '>':
5749d9b81f7Schristos if (code_blocks == 0) {
5759d9b81f7Schristos md_named("gt");
5769d9b81f7Schristos c = '\0';
5779d9b81f7Schristos }
5789d9b81f7Schristos break;
5799d9b81f7Schristos case '\\':
5809d9b81f7Schristos uc = 0;
5819d9b81f7Schristos nextfont = NULL;
5829d9b81f7Schristos switch (mandoc_escape(&s, &seq, &sz)) {
5839d9b81f7Schristos case ESCAPE_UNICODE:
5849d9b81f7Schristos uc = mchars_num2uc(seq + 1, sz - 1);
5859d9b81f7Schristos break;
5869d9b81f7Schristos case ESCAPE_NUMBERED:
5879d9b81f7Schristos uc = mchars_num2char(seq, sz);
5889d9b81f7Schristos break;
5899d9b81f7Schristos case ESCAPE_SPECIAL:
5909d9b81f7Schristos uc = mchars_spec2cp(seq, sz);
5919d9b81f7Schristos break;
592*fc3ee6fdSchristos case ESCAPE_UNDEF:
593*fc3ee6fdSchristos uc = *seq;
594*fc3ee6fdSchristos break;
595*fc3ee6fdSchristos case ESCAPE_DEVICE:
596*fc3ee6fdSchristos md_rawword("markdown");
597*fc3ee6fdSchristos continue;
5989d9b81f7Schristos case ESCAPE_FONTBOLD:
5999d9b81f7Schristos nextfont = "**";
6009d9b81f7Schristos break;
6019d9b81f7Schristos case ESCAPE_FONTITALIC:
6029d9b81f7Schristos nextfont = "*";
6039d9b81f7Schristos break;
6049d9b81f7Schristos case ESCAPE_FONTBI:
6059d9b81f7Schristos nextfont = "***";
6069d9b81f7Schristos break;
6079d9b81f7Schristos case ESCAPE_FONT:
608*fc3ee6fdSchristos case ESCAPE_FONTCW:
6099d9b81f7Schristos case ESCAPE_FONTROMAN:
6109d9b81f7Schristos nextfont = "";
6119d9b81f7Schristos break;
6129d9b81f7Schristos case ESCAPE_FONTPREV:
6139d9b81f7Schristos nextfont = prevfont;
6149d9b81f7Schristos break;
6159d9b81f7Schristos case ESCAPE_BREAK:
6169d9b81f7Schristos breakline = 1;
6179d9b81f7Schristos break;
6189d9b81f7Schristos case ESCAPE_NOSPACE:
6199d9b81f7Schristos case ESCAPE_SKIPCHAR:
6209d9b81f7Schristos case ESCAPE_OVERSTRIKE:
6219d9b81f7Schristos /* XXX not implemented */
6229d9b81f7Schristos /* FALLTHROUGH */
6239d9b81f7Schristos case ESCAPE_ERROR:
6249d9b81f7Schristos default:
6259d9b81f7Schristos break;
6269d9b81f7Schristos }
6279d9b81f7Schristos if (nextfont != NULL && !code_blocks) {
6289d9b81f7Schristos if (*currfont != '\0') {
6299d9b81f7Schristos outflags &= ~MD_spc;
6309d9b81f7Schristos md_rawword(currfont);
6319d9b81f7Schristos }
6329d9b81f7Schristos prevfont = currfont;
6339d9b81f7Schristos currfont = nextfont;
6349d9b81f7Schristos if (*currfont != '\0') {
6359d9b81f7Schristos outflags &= ~MD_spc;
6369d9b81f7Schristos md_rawword(currfont);
6379d9b81f7Schristos }
6389d9b81f7Schristos }
6399d9b81f7Schristos if (uc) {
6409d9b81f7Schristos if ((uc < 0x20 && uc != 0x09) ||
6419d9b81f7Schristos (uc > 0x7E && uc < 0xA0))
6429d9b81f7Schristos uc = 0xFFFD;
6439d9b81f7Schristos if (code_blocks) {
6449d9b81f7Schristos seq = mchars_uc2str(uc);
6459d9b81f7Schristos fputs(seq, stdout);
6469d9b81f7Schristos outcount += strlen(seq);
6479d9b81f7Schristos } else {
6489d9b81f7Schristos printf("&#%d;", uc);
6499d9b81f7Schristos outcount++;
6509d9b81f7Schristos }
6519d9b81f7Schristos escflags &= ~ESC_FON;
6529d9b81f7Schristos }
6539d9b81f7Schristos c = '\0';
6549d9b81f7Schristos break;
6559d9b81f7Schristos case ']':
6569d9b81f7Schristos bs = escflags & ESC_SQU && !code_blocks;
6579d9b81f7Schristos escflags |= ESC_HYP;
6589d9b81f7Schristos break;
6599d9b81f7Schristos default:
6609d9b81f7Schristos break;
6619d9b81f7Schristos }
6629d9b81f7Schristos if (bs)
6639d9b81f7Schristos putchar('\\');
6649d9b81f7Schristos md_char(c);
6659d9b81f7Schristos if (breakline &&
6669d9b81f7Schristos (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
6679d9b81f7Schristos printf(" \n");
6689d9b81f7Schristos breakline = 0;
6699d9b81f7Schristos while (*s == ' ' || *s == ASCII_NBRSP)
6709d9b81f7Schristos s++;
6719d9b81f7Schristos }
6729d9b81f7Schristos }
6739d9b81f7Schristos if (*currfont != '\0') {
6749d9b81f7Schristos outflags &= ~MD_spc;
6759d9b81f7Schristos md_rawword(currfont);
6769d9b81f7Schristos } else if (s[-2] == ' ')
6779d9b81f7Schristos escflags |= ESC_EOL;
6789d9b81f7Schristos else
6799d9b81f7Schristos escflags &= ~ESC_EOL;
6809d9b81f7Schristos }
6819d9b81f7Schristos
6829d9b81f7Schristos /*
6839d9b81f7Schristos * Print a single HTML named character reference.
6849d9b81f7Schristos */
6859d9b81f7Schristos static void
md_named(const char * s)6869d9b81f7Schristos md_named(const char *s)
6879d9b81f7Schristos {
6889d9b81f7Schristos printf("&%s;", s);
6899d9b81f7Schristos escflags &= ~(ESC_FON | ESC_EOL);
6909d9b81f7Schristos outcount++;
6919d9b81f7Schristos }
6929d9b81f7Schristos
6939d9b81f7Schristos /*
6949d9b81f7Schristos * Print a single raw character and maintain certain escape flags.
6959d9b81f7Schristos */
6969d9b81f7Schristos static void
md_char(unsigned char c)6979d9b81f7Schristos md_char(unsigned char c)
6989d9b81f7Schristos {
6999d9b81f7Schristos if (c != '\0') {
7009d9b81f7Schristos putchar(c);
7019d9b81f7Schristos if (c == '*')
7029d9b81f7Schristos escflags |= ESC_FON;
7039d9b81f7Schristos else
7049d9b81f7Schristos escflags &= ~ESC_FON;
7059d9b81f7Schristos outcount++;
7069d9b81f7Schristos }
7079d9b81f7Schristos if (c != ']')
7089d9b81f7Schristos escflags &= ~ESC_HYP;
7099d9b81f7Schristos if (c == ' ' || c == '\t' || c == '>')
7109d9b81f7Schristos return;
7119d9b81f7Schristos if (isdigit(c) == 0)
7129d9b81f7Schristos escflags &= ~ESC_NUM;
7139d9b81f7Schristos else if (escflags & ESC_BOL)
7149d9b81f7Schristos escflags |= ESC_NUM;
7159d9b81f7Schristos escflags &= ~ESC_BOL;
7169d9b81f7Schristos }
7179d9b81f7Schristos
7189d9b81f7Schristos static int
md_cond_head(struct roff_node * n)7199d9b81f7Schristos md_cond_head(struct roff_node *n)
7209d9b81f7Schristos {
7219d9b81f7Schristos return n->type == ROFFT_HEAD;
7229d9b81f7Schristos }
7239d9b81f7Schristos
7249d9b81f7Schristos static int
md_cond_body(struct roff_node * n)7259d9b81f7Schristos md_cond_body(struct roff_node *n)
7269d9b81f7Schristos {
7279d9b81f7Schristos return n->type == ROFFT_BODY;
7289d9b81f7Schristos }
7299d9b81f7Schristos
7309d9b81f7Schristos static int
md_pre_abort(struct roff_node * n)731*fc3ee6fdSchristos md_pre_abort(struct roff_node *n)
732*fc3ee6fdSchristos {
733*fc3ee6fdSchristos abort();
734*fc3ee6fdSchristos }
735*fc3ee6fdSchristos
736*fc3ee6fdSchristos static int
md_pre_raw(struct roff_node * n)7379d9b81f7Schristos md_pre_raw(struct roff_node *n)
7389d9b81f7Schristos {
7399d9b81f7Schristos const char *prefix;
7409d9b81f7Schristos
741*fc3ee6fdSchristos if ((prefix = md_act(n->tok)->prefix) != NULL) {
7429d9b81f7Schristos md_rawword(prefix);
7439d9b81f7Schristos outflags &= ~MD_spc;
7449d9b81f7Schristos if (*prefix == '`')
7459d9b81f7Schristos code_blocks++;
7469d9b81f7Schristos }
7479d9b81f7Schristos return 1;
7489d9b81f7Schristos }
7499d9b81f7Schristos
7509d9b81f7Schristos static void
md_post_raw(struct roff_node * n)7519d9b81f7Schristos md_post_raw(struct roff_node *n)
7529d9b81f7Schristos {
7539d9b81f7Schristos const char *suffix;
7549d9b81f7Schristos
755*fc3ee6fdSchristos if ((suffix = md_act(n->tok)->suffix) != NULL) {
7569d9b81f7Schristos outflags &= ~(MD_spc | MD_nl);
7579d9b81f7Schristos md_rawword(suffix);
7589d9b81f7Schristos if (*suffix == '`')
7599d9b81f7Schristos code_blocks--;
7609d9b81f7Schristos }
7619d9b81f7Schristos }
7629d9b81f7Schristos
7639d9b81f7Schristos static int
md_pre_word(struct roff_node * n)7649d9b81f7Schristos md_pre_word(struct roff_node *n)
7659d9b81f7Schristos {
7669d9b81f7Schristos const char *prefix;
7679d9b81f7Schristos
768*fc3ee6fdSchristos if ((prefix = md_act(n->tok)->prefix) != NULL) {
7699d9b81f7Schristos md_word(prefix);
7709d9b81f7Schristos outflags &= ~MD_spc;
7719d9b81f7Schristos }
7729d9b81f7Schristos return 1;
7739d9b81f7Schristos }
7749d9b81f7Schristos
7759d9b81f7Schristos static void
md_post_word(struct roff_node * n)7769d9b81f7Schristos md_post_word(struct roff_node *n)
7779d9b81f7Schristos {
7789d9b81f7Schristos const char *suffix;
7799d9b81f7Schristos
780*fc3ee6fdSchristos if ((suffix = md_act(n->tok)->suffix) != NULL) {
7819d9b81f7Schristos outflags &= ~(MD_spc | MD_nl);
7829d9b81f7Schristos md_word(suffix);
7839d9b81f7Schristos }
7849d9b81f7Schristos }
7859d9b81f7Schristos
7869d9b81f7Schristos static void
md_post_pc(struct roff_node * n)7879d9b81f7Schristos md_post_pc(struct roff_node *n)
7889d9b81f7Schristos {
7899d9b81f7Schristos md_post_raw(n);
7909d9b81f7Schristos if (n->parent->tok != MDOC_Rs)
7919d9b81f7Schristos return;
7929d9b81f7Schristos if (n->next != NULL) {
7939d9b81f7Schristos md_word(",");
7949d9b81f7Schristos if (n->prev != NULL &&
7959d9b81f7Schristos n->prev->tok == n->tok &&
7969d9b81f7Schristos n->next->tok == n->tok)
7979d9b81f7Schristos md_word("and");
7989d9b81f7Schristos } else {
7999d9b81f7Schristos md_word(".");
8009d9b81f7Schristos outflags |= MD_nl;
8019d9b81f7Schristos }
8029d9b81f7Schristos }
8039d9b81f7Schristos
8049d9b81f7Schristos static int
md_pre_skip(struct roff_node * n)8059d9b81f7Schristos md_pre_skip(struct roff_node *n)
8069d9b81f7Schristos {
8079d9b81f7Schristos return 0;
8089d9b81f7Schristos }
8099d9b81f7Schristos
8109d9b81f7Schristos static void
md_pre_syn(struct roff_node * n)8119d9b81f7Schristos md_pre_syn(struct roff_node *n)
8129d9b81f7Schristos {
8139d9b81f7Schristos if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
8149d9b81f7Schristos return;
8159d9b81f7Schristos
8169d9b81f7Schristos if (n->prev->tok == n->tok &&
8179d9b81f7Schristos n->tok != MDOC_Ft &&
8189d9b81f7Schristos n->tok != MDOC_Fo &&
8199d9b81f7Schristos n->tok != MDOC_Fn) {
8209d9b81f7Schristos outflags |= MD_br;
8219d9b81f7Schristos return;
8229d9b81f7Schristos }
8239d9b81f7Schristos
8249d9b81f7Schristos switch (n->prev->tok) {
8259d9b81f7Schristos case MDOC_Fd:
8269d9b81f7Schristos case MDOC_Fn:
8279d9b81f7Schristos case MDOC_Fo:
8289d9b81f7Schristos case MDOC_In:
8299d9b81f7Schristos case MDOC_Vt:
8309d9b81f7Schristos outflags |= MD_sp;
8319d9b81f7Schristos break;
8329d9b81f7Schristos case MDOC_Ft:
8339d9b81f7Schristos if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
8349d9b81f7Schristos outflags |= MD_sp;
8359d9b81f7Schristos break;
8369d9b81f7Schristos }
8379d9b81f7Schristos /* FALLTHROUGH */
8389d9b81f7Schristos default:
8399d9b81f7Schristos outflags |= MD_br;
8409d9b81f7Schristos break;
8419d9b81f7Schristos }
8429d9b81f7Schristos }
8439d9b81f7Schristos
8449d9b81f7Schristos static int
md_pre_An(struct roff_node * n)8459d9b81f7Schristos md_pre_An(struct roff_node *n)
8469d9b81f7Schristos {
8479d9b81f7Schristos switch (n->norm->An.auth) {
8489d9b81f7Schristos case AUTH_split:
8499d9b81f7Schristos outflags &= ~MD_An_nosplit;
8509d9b81f7Schristos outflags |= MD_An_split;
8519d9b81f7Schristos return 0;
8529d9b81f7Schristos case AUTH_nosplit:
8539d9b81f7Schristos outflags &= ~MD_An_split;
8549d9b81f7Schristos outflags |= MD_An_nosplit;
8559d9b81f7Schristos return 0;
8569d9b81f7Schristos default:
8579d9b81f7Schristos if (outflags & MD_An_split)
8589d9b81f7Schristos outflags |= MD_br;
8599d9b81f7Schristos else if (n->sec == SEC_AUTHORS &&
8609d9b81f7Schristos ! (outflags & MD_An_nosplit))
8619d9b81f7Schristos outflags |= MD_An_split;
8629d9b81f7Schristos return 1;
8639d9b81f7Schristos }
8649d9b81f7Schristos }
8659d9b81f7Schristos
8669d9b81f7Schristos static int
md_pre_Ap(struct roff_node * n)8679d9b81f7Schristos md_pre_Ap(struct roff_node *n)
8689d9b81f7Schristos {
8699d9b81f7Schristos outflags &= ~MD_spc;
8709d9b81f7Schristos md_word("'");
8719d9b81f7Schristos outflags &= ~MD_spc;
8729d9b81f7Schristos return 0;
8739d9b81f7Schristos }
8749d9b81f7Schristos
8759d9b81f7Schristos static int
md_pre_Bd(struct roff_node * n)8769d9b81f7Schristos md_pre_Bd(struct roff_node *n)
8779d9b81f7Schristos {
8789d9b81f7Schristos switch (n->norm->Bd.type) {
8799d9b81f7Schristos case DISP_unfilled:
8809d9b81f7Schristos case DISP_literal:
8819d9b81f7Schristos return md_pre_Dl(n);
8829d9b81f7Schristos default:
8839d9b81f7Schristos return md_pre_D1(n);
8849d9b81f7Schristos }
8859d9b81f7Schristos }
8869d9b81f7Schristos
8879d9b81f7Schristos static int
md_pre_Bk(struct roff_node * n)8889d9b81f7Schristos md_pre_Bk(struct roff_node *n)
8899d9b81f7Schristos {
8909d9b81f7Schristos switch (n->type) {
8919d9b81f7Schristos case ROFFT_BLOCK:
8929d9b81f7Schristos return 1;
8939d9b81f7Schristos case ROFFT_BODY:
8949d9b81f7Schristos outflags |= MD_Bk;
8959d9b81f7Schristos return 1;
8969d9b81f7Schristos default:
8979d9b81f7Schristos return 0;
8989d9b81f7Schristos }
8999d9b81f7Schristos }
9009d9b81f7Schristos
9019d9b81f7Schristos static void
md_post_Bk(struct roff_node * n)9029d9b81f7Schristos md_post_Bk(struct roff_node *n)
9039d9b81f7Schristos {
9049d9b81f7Schristos if (n->type == ROFFT_BODY)
9059d9b81f7Schristos outflags &= ~MD_Bk;
9069d9b81f7Schristos }
9079d9b81f7Schristos
9089d9b81f7Schristos static int
md_pre_Bl(struct roff_node * n)9099d9b81f7Schristos md_pre_Bl(struct roff_node *n)
9109d9b81f7Schristos {
9119d9b81f7Schristos n->norm->Bl.count = 0;
9129d9b81f7Schristos if (n->norm->Bl.type == LIST_column)
9139d9b81f7Schristos md_pre_Dl(n);
9149d9b81f7Schristos outflags |= MD_sp;
9159d9b81f7Schristos return 1;
9169d9b81f7Schristos }
9179d9b81f7Schristos
9189d9b81f7Schristos static void
md_post_Bl(struct roff_node * n)9199d9b81f7Schristos md_post_Bl(struct roff_node *n)
9209d9b81f7Schristos {
9219d9b81f7Schristos n->norm->Bl.count = 0;
9229d9b81f7Schristos if (n->norm->Bl.type == LIST_column)
9239d9b81f7Schristos md_post_D1(n);
9249d9b81f7Schristos outflags |= MD_sp;
9259d9b81f7Schristos }
9269d9b81f7Schristos
9279d9b81f7Schristos static int
md_pre_D1(struct roff_node * n)9289d9b81f7Schristos md_pre_D1(struct roff_node *n)
9299d9b81f7Schristos {
9309d9b81f7Schristos /*
9319d9b81f7Schristos * Markdown blockquote syntax does not work inside code blocks.
9329d9b81f7Schristos * The best we can do is fall back to another nested code block.
9339d9b81f7Schristos */
9349d9b81f7Schristos if (code_blocks) {
9359d9b81f7Schristos md_stack('\t');
9369d9b81f7Schristos code_blocks++;
9379d9b81f7Schristos } else {
9389d9b81f7Schristos md_stack('>');
9399d9b81f7Schristos quote_blocks++;
9409d9b81f7Schristos }
9419d9b81f7Schristos outflags |= MD_sp;
9429d9b81f7Schristos return 1;
9439d9b81f7Schristos }
9449d9b81f7Schristos
9459d9b81f7Schristos static void
md_post_D1(struct roff_node * n)9469d9b81f7Schristos md_post_D1(struct roff_node *n)
9479d9b81f7Schristos {
9489d9b81f7Schristos md_stack((char)-1);
9499d9b81f7Schristos if (code_blocks)
9509d9b81f7Schristos code_blocks--;
9519d9b81f7Schristos else
9529d9b81f7Schristos quote_blocks--;
9539d9b81f7Schristos outflags |= MD_sp;
9549d9b81f7Schristos }
9559d9b81f7Schristos
9569d9b81f7Schristos static int
md_pre_Dl(struct roff_node * n)9579d9b81f7Schristos md_pre_Dl(struct roff_node *n)
9589d9b81f7Schristos {
9599d9b81f7Schristos /*
9609d9b81f7Schristos * Markdown code block syntax does not work inside blockquotes.
9619d9b81f7Schristos * The best we can do is fall back to another nested blockquote.
9629d9b81f7Schristos */
9639d9b81f7Schristos if (quote_blocks) {
9649d9b81f7Schristos md_stack('>');
9659d9b81f7Schristos quote_blocks++;
9669d9b81f7Schristos } else {
9679d9b81f7Schristos md_stack('\t');
9689d9b81f7Schristos code_blocks++;
9699d9b81f7Schristos }
9709d9b81f7Schristos outflags |= MD_sp;
9719d9b81f7Schristos return 1;
9729d9b81f7Schristos }
9739d9b81f7Schristos
9749d9b81f7Schristos static int
md_pre_En(struct roff_node * n)9759d9b81f7Schristos md_pre_En(struct roff_node *n)
9769d9b81f7Schristos {
9779d9b81f7Schristos if (n->norm->Es == NULL ||
9789d9b81f7Schristos n->norm->Es->child == NULL)
9799d9b81f7Schristos return 1;
9809d9b81f7Schristos
9819d9b81f7Schristos md_word(n->norm->Es->child->string);
9829d9b81f7Schristos outflags &= ~MD_spc;
9839d9b81f7Schristos return 1;
9849d9b81f7Schristos }
9859d9b81f7Schristos
9869d9b81f7Schristos static void
md_post_En(struct roff_node * n)9879d9b81f7Schristos md_post_En(struct roff_node *n)
9889d9b81f7Schristos {
9899d9b81f7Schristos if (n->norm->Es == NULL ||
9909d9b81f7Schristos n->norm->Es->child == NULL ||
9919d9b81f7Schristos n->norm->Es->child->next == NULL)
9929d9b81f7Schristos return;
9939d9b81f7Schristos
9949d9b81f7Schristos outflags &= ~MD_spc;
9959d9b81f7Schristos md_word(n->norm->Es->child->next->string);
9969d9b81f7Schristos }
9979d9b81f7Schristos
9989d9b81f7Schristos static int
md_pre_Eo(struct roff_node * n)9999d9b81f7Schristos md_pre_Eo(struct roff_node *n)
10009d9b81f7Schristos {
10019d9b81f7Schristos if (n->end == ENDBODY_NOT &&
10029d9b81f7Schristos n->parent->head->child == NULL &&
10039d9b81f7Schristos n->child != NULL &&
10049d9b81f7Schristos n->child->end != ENDBODY_NOT)
10059d9b81f7Schristos md_preword();
10069d9b81f7Schristos else if (n->end != ENDBODY_NOT ? n->child != NULL :
10079d9b81f7Schristos n->parent->head->child != NULL && (n->child != NULL ||
10089d9b81f7Schristos (n->parent->tail != NULL && n->parent->tail->child != NULL)))
10099d9b81f7Schristos outflags &= ~(MD_spc | MD_nl);
10109d9b81f7Schristos return 1;
10119d9b81f7Schristos }
10129d9b81f7Schristos
10139d9b81f7Schristos static void
md_post_Eo(struct roff_node * n)10149d9b81f7Schristos md_post_Eo(struct roff_node *n)
10159d9b81f7Schristos {
10169d9b81f7Schristos if (n->end != ENDBODY_NOT) {
10179d9b81f7Schristos outflags |= MD_spc;
10189d9b81f7Schristos return;
10199d9b81f7Schristos }
10209d9b81f7Schristos
10219d9b81f7Schristos if (n->child == NULL && n->parent->head->child == NULL)
10229d9b81f7Schristos return;
10239d9b81f7Schristos
10249d9b81f7Schristos if (n->parent->tail != NULL && n->parent->tail->child != NULL)
10259d9b81f7Schristos outflags &= ~MD_spc;
10269d9b81f7Schristos else
10279d9b81f7Schristos outflags |= MD_spc;
10289d9b81f7Schristos }
10299d9b81f7Schristos
10309d9b81f7Schristos static int
md_pre_Fa(struct roff_node * n)10319d9b81f7Schristos md_pre_Fa(struct roff_node *n)
10329d9b81f7Schristos {
10339d9b81f7Schristos int am_Fa;
10349d9b81f7Schristos
10359d9b81f7Schristos am_Fa = n->tok == MDOC_Fa;
10369d9b81f7Schristos
10379d9b81f7Schristos if (am_Fa)
10389d9b81f7Schristos n = n->child;
10399d9b81f7Schristos
10409d9b81f7Schristos while (n != NULL) {
10419d9b81f7Schristos md_rawword("*");
10429d9b81f7Schristos outflags &= ~MD_spc;
10439d9b81f7Schristos md_node(n);
10449d9b81f7Schristos outflags &= ~MD_spc;
10459d9b81f7Schristos md_rawword("*");
10469d9b81f7Schristos if ((n = n->next) != NULL)
10479d9b81f7Schristos md_word(",");
10489d9b81f7Schristos }
10499d9b81f7Schristos return 0;
10509d9b81f7Schristos }
10519d9b81f7Schristos
10529d9b81f7Schristos static void
md_post_Fa(struct roff_node * n)10539d9b81f7Schristos md_post_Fa(struct roff_node *n)
10549d9b81f7Schristos {
10559d9b81f7Schristos if (n->next != NULL && n->next->tok == MDOC_Fa)
10569d9b81f7Schristos md_word(",");
10579d9b81f7Schristos }
10589d9b81f7Schristos
10599d9b81f7Schristos static int
md_pre_Fd(struct roff_node * n)10609d9b81f7Schristos md_pre_Fd(struct roff_node *n)
10619d9b81f7Schristos {
10629d9b81f7Schristos md_pre_syn(n);
10639d9b81f7Schristos md_pre_raw(n);
10649d9b81f7Schristos return 1;
10659d9b81f7Schristos }
10669d9b81f7Schristos
10679d9b81f7Schristos static void
md_post_Fd(struct roff_node * n)10689d9b81f7Schristos md_post_Fd(struct roff_node *n)
10699d9b81f7Schristos {
10709d9b81f7Schristos md_post_raw(n);
10719d9b81f7Schristos outflags |= MD_br;
10729d9b81f7Schristos }
10739d9b81f7Schristos
10749d9b81f7Schristos static void
md_post_Fl(struct roff_node * n)10759d9b81f7Schristos md_post_Fl(struct roff_node *n)
10769d9b81f7Schristos {
10779d9b81f7Schristos md_post_raw(n);
10789d9b81f7Schristos if (n->child == NULL && n->next != NULL &&
10799d9b81f7Schristos n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
10809d9b81f7Schristos outflags &= ~MD_spc;
10819d9b81f7Schristos }
10829d9b81f7Schristos
10839d9b81f7Schristos static int
md_pre_Fn(struct roff_node * n)10849d9b81f7Schristos md_pre_Fn(struct roff_node *n)
10859d9b81f7Schristos {
10869d9b81f7Schristos md_pre_syn(n);
10879d9b81f7Schristos
10889d9b81f7Schristos if ((n = n->child) == NULL)
10899d9b81f7Schristos return 0;
10909d9b81f7Schristos
10919d9b81f7Schristos md_rawword("**");
10929d9b81f7Schristos outflags &= ~MD_spc;
10939d9b81f7Schristos md_node(n);
10949d9b81f7Schristos outflags &= ~MD_spc;
10959d9b81f7Schristos md_rawword("**");
10969d9b81f7Schristos outflags &= ~MD_spc;
10979d9b81f7Schristos md_word("(");
10989d9b81f7Schristos
10999d9b81f7Schristos if ((n = n->next) != NULL)
11009d9b81f7Schristos md_pre_Fa(n);
11019d9b81f7Schristos return 0;
11029d9b81f7Schristos }
11039d9b81f7Schristos
11049d9b81f7Schristos static void
md_post_Fn(struct roff_node * n)11059d9b81f7Schristos md_post_Fn(struct roff_node *n)
11069d9b81f7Schristos {
11079d9b81f7Schristos md_word(")");
11089d9b81f7Schristos if (n->flags & NODE_SYNPRETTY) {
11099d9b81f7Schristos md_word(";");
11109d9b81f7Schristos outflags |= MD_sp;
11119d9b81f7Schristos }
11129d9b81f7Schristos }
11139d9b81f7Schristos
11149d9b81f7Schristos static int
md_pre_Fo(struct roff_node * n)11159d9b81f7Schristos md_pre_Fo(struct roff_node *n)
11169d9b81f7Schristos {
11179d9b81f7Schristos switch (n->type) {
11189d9b81f7Schristos case ROFFT_BLOCK:
11199d9b81f7Schristos md_pre_syn(n);
11209d9b81f7Schristos break;
11219d9b81f7Schristos case ROFFT_HEAD:
11229d9b81f7Schristos if (n->child == NULL)
11239d9b81f7Schristos return 0;
11249d9b81f7Schristos md_pre_raw(n);
11259d9b81f7Schristos break;
11269d9b81f7Schristos case ROFFT_BODY:
11279d9b81f7Schristos outflags &= ~(MD_spc | MD_nl);
11289d9b81f7Schristos md_word("(");
11299d9b81f7Schristos break;
11309d9b81f7Schristos default:
11319d9b81f7Schristos break;
11329d9b81f7Schristos }
11339d9b81f7Schristos return 1;
11349d9b81f7Schristos }
11359d9b81f7Schristos
11369d9b81f7Schristos static void
md_post_Fo(struct roff_node * n)11379d9b81f7Schristos md_post_Fo(struct roff_node *n)
11389d9b81f7Schristos {
11399d9b81f7Schristos switch (n->type) {
11409d9b81f7Schristos case ROFFT_HEAD:
11419d9b81f7Schristos if (n->child != NULL)
11429d9b81f7Schristos md_post_raw(n);
11439d9b81f7Schristos break;
11449d9b81f7Schristos case ROFFT_BODY:
11459d9b81f7Schristos md_post_Fn(n);
11469d9b81f7Schristos break;
11479d9b81f7Schristos default:
11489d9b81f7Schristos break;
11499d9b81f7Schristos }
11509d9b81f7Schristos }
11519d9b81f7Schristos
11529d9b81f7Schristos static int
md_pre_In(struct roff_node * n)11539d9b81f7Schristos md_pre_In(struct roff_node *n)
11549d9b81f7Schristos {
11559d9b81f7Schristos if (n->flags & NODE_SYNPRETTY) {
11569d9b81f7Schristos md_pre_syn(n);
11579d9b81f7Schristos md_rawword("**");
11589d9b81f7Schristos outflags &= ~MD_spc;
11599d9b81f7Schristos md_word("#include <");
11609d9b81f7Schristos } else {
11619d9b81f7Schristos md_word("<");
11629d9b81f7Schristos outflags &= ~MD_spc;
11639d9b81f7Schristos md_rawword("*");
11649d9b81f7Schristos }
11659d9b81f7Schristos outflags &= ~MD_spc;
11669d9b81f7Schristos return 1;
11679d9b81f7Schristos }
11689d9b81f7Schristos
11699d9b81f7Schristos static void
md_post_In(struct roff_node * n)11709d9b81f7Schristos md_post_In(struct roff_node *n)
11719d9b81f7Schristos {
11729d9b81f7Schristos if (n->flags & NODE_SYNPRETTY) {
11739d9b81f7Schristos outflags &= ~MD_spc;
11749d9b81f7Schristos md_rawword(">**");
11759d9b81f7Schristos outflags |= MD_nl;
11769d9b81f7Schristos } else {
11779d9b81f7Schristos outflags &= ~MD_spc;
11789d9b81f7Schristos md_rawword("*>");
11799d9b81f7Schristos }
11809d9b81f7Schristos }
11819d9b81f7Schristos
11829d9b81f7Schristos static int
md_pre_It(struct roff_node * n)11839d9b81f7Schristos md_pre_It(struct roff_node *n)
11849d9b81f7Schristos {
11859d9b81f7Schristos struct roff_node *bln;
11869d9b81f7Schristos
11879d9b81f7Schristos switch (n->type) {
11889d9b81f7Schristos case ROFFT_BLOCK:
11899d9b81f7Schristos return 1;
11909d9b81f7Schristos
11919d9b81f7Schristos case ROFFT_HEAD:
11929d9b81f7Schristos bln = n->parent->parent;
11939d9b81f7Schristos if (bln->norm->Bl.comp == 0 &&
11949d9b81f7Schristos bln->norm->Bl.type != LIST_column)
11959d9b81f7Schristos outflags |= MD_sp;
11969d9b81f7Schristos outflags |= MD_nl;
11979d9b81f7Schristos
11989d9b81f7Schristos switch (bln->norm->Bl.type) {
11999d9b81f7Schristos case LIST_item:
12009d9b81f7Schristos outflags |= MD_br;
12019d9b81f7Schristos return 0;
12029d9b81f7Schristos case LIST_inset:
12039d9b81f7Schristos case LIST_diag:
12049d9b81f7Schristos case LIST_ohang:
12059d9b81f7Schristos outflags |= MD_br;
12069d9b81f7Schristos return 1;
12079d9b81f7Schristos case LIST_tag:
12089d9b81f7Schristos case LIST_hang:
12099d9b81f7Schristos outflags |= MD_sp;
12109d9b81f7Schristos return 1;
12119d9b81f7Schristos case LIST_bullet:
12129d9b81f7Schristos md_rawword("*\t");
12139d9b81f7Schristos break;
12149d9b81f7Schristos case LIST_dash:
12159d9b81f7Schristos case LIST_hyphen:
12169d9b81f7Schristos md_rawword("-\t");
12179d9b81f7Schristos break;
12189d9b81f7Schristos case LIST_enum:
12199d9b81f7Schristos md_preword();
12209d9b81f7Schristos if (bln->norm->Bl.count < 99)
12219d9b81f7Schristos bln->norm->Bl.count++;
12229d9b81f7Schristos printf("%d.\t", bln->norm->Bl.count);
12239d9b81f7Schristos escflags &= ~ESC_FON;
12249d9b81f7Schristos break;
12259d9b81f7Schristos case LIST_column:
12269d9b81f7Schristos outflags |= MD_br;
12279d9b81f7Schristos return 0;
12289d9b81f7Schristos default:
12299d9b81f7Schristos return 0;
12309d9b81f7Schristos }
12319d9b81f7Schristos outflags &= ~MD_spc;
12329d9b81f7Schristos outflags |= MD_nonl;
12339d9b81f7Schristos outcount = 0;
12349d9b81f7Schristos md_stack('\t');
12359d9b81f7Schristos if (code_blocks || quote_blocks)
12369d9b81f7Schristos list_blocks++;
12379d9b81f7Schristos return 0;
12389d9b81f7Schristos
12399d9b81f7Schristos case ROFFT_BODY:
12409d9b81f7Schristos bln = n->parent->parent;
12419d9b81f7Schristos switch (bln->norm->Bl.type) {
12429d9b81f7Schristos case LIST_ohang:
12439d9b81f7Schristos outflags |= MD_br;
12449d9b81f7Schristos break;
12459d9b81f7Schristos case LIST_tag:
12469d9b81f7Schristos case LIST_hang:
12479d9b81f7Schristos md_pre_D1(n);
12489d9b81f7Schristos break;
12499d9b81f7Schristos default:
12509d9b81f7Schristos break;
12519d9b81f7Schristos }
12529d9b81f7Schristos return 1;
12539d9b81f7Schristos
12549d9b81f7Schristos default:
12559d9b81f7Schristos return 0;
12569d9b81f7Schristos }
12579d9b81f7Schristos }
12589d9b81f7Schristos
12599d9b81f7Schristos static void
md_post_It(struct roff_node * n)12609d9b81f7Schristos md_post_It(struct roff_node *n)
12619d9b81f7Schristos {
12629d9b81f7Schristos struct roff_node *bln;
12639d9b81f7Schristos int i, nc;
12649d9b81f7Schristos
12659d9b81f7Schristos if (n->type != ROFFT_BODY)
12669d9b81f7Schristos return;
12679d9b81f7Schristos
12689d9b81f7Schristos bln = n->parent->parent;
12699d9b81f7Schristos switch (bln->norm->Bl.type) {
12709d9b81f7Schristos case LIST_bullet:
12719d9b81f7Schristos case LIST_dash:
12729d9b81f7Schristos case LIST_hyphen:
12739d9b81f7Schristos case LIST_enum:
12749d9b81f7Schristos md_stack((char)-1);
12759d9b81f7Schristos if (code_blocks || quote_blocks)
12769d9b81f7Schristos list_blocks--;
12779d9b81f7Schristos break;
12789d9b81f7Schristos case LIST_tag:
12799d9b81f7Schristos case LIST_hang:
12809d9b81f7Schristos md_post_D1(n);
12819d9b81f7Schristos break;
12829d9b81f7Schristos
12839d9b81f7Schristos case LIST_column:
12849d9b81f7Schristos if (n->next == NULL)
12859d9b81f7Schristos break;
12869d9b81f7Schristos
12879d9b81f7Schristos /* Calculate the array index of the current column. */
12889d9b81f7Schristos
12899d9b81f7Schristos i = 0;
12909d9b81f7Schristos while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
12919d9b81f7Schristos i++;
12929d9b81f7Schristos
12939d9b81f7Schristos /*
12949d9b81f7Schristos * If a width was specified for this column,
12959d9b81f7Schristos * subtract what printed, and
12969d9b81f7Schristos * add the same spacing as in mdoc_term.c.
12979d9b81f7Schristos */
12989d9b81f7Schristos
12999d9b81f7Schristos nc = bln->norm->Bl.ncols;
13009d9b81f7Schristos i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
13019d9b81f7Schristos (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
13029d9b81f7Schristos if (i < 1)
13039d9b81f7Schristos i = 1;
13049d9b81f7Schristos while (i-- > 0)
13059d9b81f7Schristos putchar(' ');
13069d9b81f7Schristos
13079d9b81f7Schristos outflags &= ~MD_spc;
13089d9b81f7Schristos escflags &= ~ESC_FON;
13099d9b81f7Schristos outcount = 0;
13109d9b81f7Schristos break;
13119d9b81f7Schristos
13129d9b81f7Schristos default:
13139d9b81f7Schristos break;
13149d9b81f7Schristos }
13159d9b81f7Schristos }
13169d9b81f7Schristos
13179d9b81f7Schristos static void
md_post_Lb(struct roff_node * n)13189d9b81f7Schristos md_post_Lb(struct roff_node *n)
13199d9b81f7Schristos {
13209d9b81f7Schristos if (n->sec == SEC_LIBRARY)
13219d9b81f7Schristos outflags |= MD_br;
13229d9b81f7Schristos }
13239d9b81f7Schristos
13249d9b81f7Schristos static void
md_uri(const char * s)13259d9b81f7Schristos md_uri(const char *s)
13269d9b81f7Schristos {
13279d9b81f7Schristos while (*s != '\0') {
13289d9b81f7Schristos if (strchr("%()<>", *s) != NULL) {
13299d9b81f7Schristos printf("%%%2.2hhX", *s);
13309d9b81f7Schristos outcount += 3;
13319d9b81f7Schristos } else {
13329d9b81f7Schristos putchar(*s);
13339d9b81f7Schristos outcount++;
13349d9b81f7Schristos }
13359d9b81f7Schristos s++;
13369d9b81f7Schristos }
13379d9b81f7Schristos }
13389d9b81f7Schristos
13399d9b81f7Schristos static int
md_pre_Lk(struct roff_node * n)13409d9b81f7Schristos md_pre_Lk(struct roff_node *n)
13419d9b81f7Schristos {
13429d9b81f7Schristos const struct roff_node *link, *descr, *punct;
13439d9b81f7Schristos
13449d9b81f7Schristos if ((link = n->child) == NULL)
13459d9b81f7Schristos return 0;
13469d9b81f7Schristos
13479d9b81f7Schristos /* Find beginning of trailing punctuation. */
13489d9b81f7Schristos punct = n->last;
13499d9b81f7Schristos while (punct != link && punct->flags & NODE_DELIMC)
13509d9b81f7Schristos punct = punct->prev;
13519d9b81f7Schristos punct = punct->next;
13529d9b81f7Schristos
13539d9b81f7Schristos /* Link text. */
13549d9b81f7Schristos descr = link->next;
13559d9b81f7Schristos if (descr == punct)
13569d9b81f7Schristos descr = link; /* no text */
13579d9b81f7Schristos md_rawword("[");
13589d9b81f7Schristos outflags &= ~MD_spc;
13599d9b81f7Schristos do {
13609d9b81f7Schristos md_word(descr->string);
13619d9b81f7Schristos descr = descr->next;
13629d9b81f7Schristos } while (descr != punct);
13639d9b81f7Schristos outflags &= ~MD_spc;
13649d9b81f7Schristos
13659d9b81f7Schristos /* Link target. */
13669d9b81f7Schristos md_rawword("](");
13679d9b81f7Schristos md_uri(link->string);
13689d9b81f7Schristos outflags &= ~MD_spc;
13699d9b81f7Schristos md_rawword(")");
13709d9b81f7Schristos
13719d9b81f7Schristos /* Trailing punctuation. */
13729d9b81f7Schristos while (punct != NULL) {
13739d9b81f7Schristos md_word(punct->string);
13749d9b81f7Schristos punct = punct->next;
13759d9b81f7Schristos }
13769d9b81f7Schristos return 0;
13779d9b81f7Schristos }
13789d9b81f7Schristos
13799d9b81f7Schristos static int
md_pre_Mt(struct roff_node * n)13809d9b81f7Schristos md_pre_Mt(struct roff_node *n)
13819d9b81f7Schristos {
13829d9b81f7Schristos const struct roff_node *nch;
13839d9b81f7Schristos
13849d9b81f7Schristos md_rawword("[");
13859d9b81f7Schristos outflags &= ~MD_spc;
13869d9b81f7Schristos for (nch = n->child; nch != NULL; nch = nch->next)
13879d9b81f7Schristos md_word(nch->string);
13889d9b81f7Schristos outflags &= ~MD_spc;
13899d9b81f7Schristos md_rawword("](mailto:");
13909d9b81f7Schristos for (nch = n->child; nch != NULL; nch = nch->next) {
13919d9b81f7Schristos md_uri(nch->string);
13929d9b81f7Schristos if (nch->next != NULL) {
13939d9b81f7Schristos putchar(' ');
13949d9b81f7Schristos outcount++;
13959d9b81f7Schristos }
13969d9b81f7Schristos }
13979d9b81f7Schristos outflags &= ~MD_spc;
13989d9b81f7Schristos md_rawword(")");
13999d9b81f7Schristos return 0;
14009d9b81f7Schristos }
14019d9b81f7Schristos
14029d9b81f7Schristos static int
md_pre_Nd(struct roff_node * n)14039d9b81f7Schristos md_pre_Nd(struct roff_node *n)
14049d9b81f7Schristos {
14059d9b81f7Schristos outflags &= ~MD_nl;
14069d9b81f7Schristos outflags |= MD_spc;
14079d9b81f7Schristos md_word("-");
14089d9b81f7Schristos return 1;
14099d9b81f7Schristos }
14109d9b81f7Schristos
14119d9b81f7Schristos static int
md_pre_Nm(struct roff_node * n)14129d9b81f7Schristos md_pre_Nm(struct roff_node *n)
14139d9b81f7Schristos {
14149d9b81f7Schristos switch (n->type) {
14159d9b81f7Schristos case ROFFT_BLOCK:
14169d9b81f7Schristos outflags |= MD_Bk;
14179d9b81f7Schristos md_pre_syn(n);
14189d9b81f7Schristos break;
14199d9b81f7Schristos case ROFFT_HEAD:
14209d9b81f7Schristos case ROFFT_ELEM:
14219d9b81f7Schristos md_pre_raw(n);
14229d9b81f7Schristos break;
14239d9b81f7Schristos default:
14249d9b81f7Schristos break;
14259d9b81f7Schristos }
14269d9b81f7Schristos return 1;
14279d9b81f7Schristos }
14289d9b81f7Schristos
14299d9b81f7Schristos static void
md_post_Nm(struct roff_node * n)14309d9b81f7Schristos md_post_Nm(struct roff_node *n)
14319d9b81f7Schristos {
14329d9b81f7Schristos switch (n->type) {
14339d9b81f7Schristos case ROFFT_BLOCK:
14349d9b81f7Schristos outflags &= ~MD_Bk;
14359d9b81f7Schristos break;
14369d9b81f7Schristos case ROFFT_HEAD:
14379d9b81f7Schristos case ROFFT_ELEM:
14389d9b81f7Schristos md_post_raw(n);
14399d9b81f7Schristos break;
14409d9b81f7Schristos default:
14419d9b81f7Schristos break;
14429d9b81f7Schristos }
14439d9b81f7Schristos }
14449d9b81f7Schristos
14459d9b81f7Schristos static int
md_pre_No(struct roff_node * n)14469d9b81f7Schristos md_pre_No(struct roff_node *n)
14479d9b81f7Schristos {
14489d9b81f7Schristos outflags |= MD_spc_force;
14499d9b81f7Schristos return 1;
14509d9b81f7Schristos }
14519d9b81f7Schristos
14529d9b81f7Schristos static int
md_pre_Ns(struct roff_node * n)14539d9b81f7Schristos md_pre_Ns(struct roff_node *n)
14549d9b81f7Schristos {
14559d9b81f7Schristos outflags &= ~MD_spc;
14569d9b81f7Schristos return 0;
14579d9b81f7Schristos }
14589d9b81f7Schristos
14599d9b81f7Schristos static void
md_post_Pf(struct roff_node * n)14609d9b81f7Schristos md_post_Pf(struct roff_node *n)
14619d9b81f7Schristos {
14629d9b81f7Schristos if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
14639d9b81f7Schristos outflags &= ~MD_spc;
14649d9b81f7Schristos }
14659d9b81f7Schristos
14669d9b81f7Schristos static int
md_pre_Pp(struct roff_node * n)14679d9b81f7Schristos md_pre_Pp(struct roff_node *n)
14689d9b81f7Schristos {
14699d9b81f7Schristos outflags |= MD_sp;
14709d9b81f7Schristos return 0;
14719d9b81f7Schristos }
14729d9b81f7Schristos
14739d9b81f7Schristos static int
md_pre_Rs(struct roff_node * n)14749d9b81f7Schristos md_pre_Rs(struct roff_node *n)
14759d9b81f7Schristos {
14769d9b81f7Schristos if (n->sec == SEC_SEE_ALSO)
14779d9b81f7Schristos outflags |= MD_sp;
14789d9b81f7Schristos return 1;
14799d9b81f7Schristos }
14809d9b81f7Schristos
14819d9b81f7Schristos static int
md_pre_Sh(struct roff_node * n)14829d9b81f7Schristos md_pre_Sh(struct roff_node *n)
14839d9b81f7Schristos {
14849d9b81f7Schristos switch (n->type) {
14859d9b81f7Schristos case ROFFT_BLOCK:
14869d9b81f7Schristos if (n->sec == SEC_AUTHORS)
14879d9b81f7Schristos outflags &= ~(MD_An_split | MD_An_nosplit);
14889d9b81f7Schristos break;
14899d9b81f7Schristos case ROFFT_HEAD:
14909d9b81f7Schristos outflags |= MD_sp;
14919d9b81f7Schristos md_rawword(n->tok == MDOC_Sh ? "#" : "##");
14929d9b81f7Schristos break;
14939d9b81f7Schristos case ROFFT_BODY:
14949d9b81f7Schristos outflags |= MD_sp;
14959d9b81f7Schristos break;
14969d9b81f7Schristos default:
14979d9b81f7Schristos break;
14989d9b81f7Schristos }
14999d9b81f7Schristos return 1;
15009d9b81f7Schristos }
15019d9b81f7Schristos
15029d9b81f7Schristos static int
md_pre_Sm(struct roff_node * n)15039d9b81f7Schristos md_pre_Sm(struct roff_node *n)
15049d9b81f7Schristos {
15059d9b81f7Schristos if (n->child == NULL)
15069d9b81f7Schristos outflags ^= MD_Sm;
15079d9b81f7Schristos else if (strcmp("on", n->child->string) == 0)
15089d9b81f7Schristos outflags |= MD_Sm;
15099d9b81f7Schristos else
15109d9b81f7Schristos outflags &= ~MD_Sm;
15119d9b81f7Schristos
15129d9b81f7Schristos if (outflags & MD_Sm)
15139d9b81f7Schristos outflags |= MD_spc;
15149d9b81f7Schristos
15159d9b81f7Schristos return 0;
15169d9b81f7Schristos }
15179d9b81f7Schristos
15189d9b81f7Schristos static int
md_pre_Vt(struct roff_node * n)15199d9b81f7Schristos md_pre_Vt(struct roff_node *n)
15209d9b81f7Schristos {
15219d9b81f7Schristos switch (n->type) {
15229d9b81f7Schristos case ROFFT_BLOCK:
15239d9b81f7Schristos md_pre_syn(n);
15249d9b81f7Schristos return 1;
15259d9b81f7Schristos case ROFFT_BODY:
15269d9b81f7Schristos case ROFFT_ELEM:
15279d9b81f7Schristos md_pre_raw(n);
15289d9b81f7Schristos return 1;
15299d9b81f7Schristos default:
15309d9b81f7Schristos return 0;
15319d9b81f7Schristos }
15329d9b81f7Schristos }
15339d9b81f7Schristos
15349d9b81f7Schristos static void
md_post_Vt(struct roff_node * n)15359d9b81f7Schristos md_post_Vt(struct roff_node *n)
15369d9b81f7Schristos {
15379d9b81f7Schristos switch (n->type) {
15389d9b81f7Schristos case ROFFT_BODY:
15399d9b81f7Schristos case ROFFT_ELEM:
15409d9b81f7Schristos md_post_raw(n);
15419d9b81f7Schristos break;
15429d9b81f7Schristos default:
15439d9b81f7Schristos break;
15449d9b81f7Schristos }
15459d9b81f7Schristos }
15469d9b81f7Schristos
15479d9b81f7Schristos static int
md_pre_Xr(struct roff_node * n)15489d9b81f7Schristos md_pre_Xr(struct roff_node *n)
15499d9b81f7Schristos {
15509d9b81f7Schristos n = n->child;
15519d9b81f7Schristos if (n == NULL)
15529d9b81f7Schristos return 0;
15539d9b81f7Schristos md_node(n);
15549d9b81f7Schristos n = n->next;
15559d9b81f7Schristos if (n == NULL)
15569d9b81f7Schristos return 0;
15579d9b81f7Schristos outflags &= ~MD_spc;
15589d9b81f7Schristos md_word("(");
15599d9b81f7Schristos md_node(n);
15609d9b81f7Schristos md_word(")");
15619d9b81f7Schristos return 0;
15629d9b81f7Schristos }
15639d9b81f7Schristos
15649d9b81f7Schristos static int
md_pre__T(struct roff_node * n)15659d9b81f7Schristos md_pre__T(struct roff_node *n)
15669d9b81f7Schristos {
15679d9b81f7Schristos if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
15689d9b81f7Schristos md_word("\"");
15699d9b81f7Schristos else
15709d9b81f7Schristos md_rawword("*");
15719d9b81f7Schristos outflags &= ~MD_spc;
15729d9b81f7Schristos return 1;
15739d9b81f7Schristos }
15749d9b81f7Schristos
15759d9b81f7Schristos static void
md_post__T(struct roff_node * n)15769d9b81f7Schristos md_post__T(struct roff_node *n)
15779d9b81f7Schristos {
15789d9b81f7Schristos outflags &= ~MD_spc;
15799d9b81f7Schristos if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
15809d9b81f7Schristos md_word("\"");
15819d9b81f7Schristos else
15829d9b81f7Schristos md_rawword("*");
15839d9b81f7Schristos md_post_pc(n);
15849d9b81f7Schristos }
15859d9b81f7Schristos
15869d9b81f7Schristos static int
md_pre_br(struct roff_node * n)15879d9b81f7Schristos md_pre_br(struct roff_node *n)
15889d9b81f7Schristos {
15899d9b81f7Schristos outflags |= MD_br;
15909d9b81f7Schristos return 0;
15919d9b81f7Schristos }
1592