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