GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_markdown.c Lines: 617 657 93.9 %
Date: 2017-11-07 Branches: 403 504 80.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mdoc_markdown.c,v 1.23 2017/06/14 01:31:19 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <sys/types.h>
18
19
#include <assert.h>
20
#include <ctype.h>
21
#include <stdio.h>
22
#include <string.h>
23
24
#include "mandoc_aux.h"
25
#include "mandoc.h"
26
#include "roff.h"
27
#include "mdoc.h"
28
#include "main.h"
29
30
struct	md_act {
31
	int		(*cond)(struct roff_node *n);
32
	int		(*pre)(struct roff_node *n);
33
	void		(*post)(struct roff_node *n);
34
	const char	 *prefix; /* pre-node string constant */
35
	const char	 *suffix; /* post-node string constant */
36
};
37
38
static	void	 md_nodelist(struct roff_node *);
39
static	void	 md_node(struct roff_node *);
40
static	const char *md_stack(char c);
41
static	void	 md_preword(void);
42
static	void	 md_rawword(const char *);
43
static	void	 md_word(const char *);
44
static	void	 md_named(const char *);
45
static	void	 md_char(unsigned char);
46
static	void	 md_uri(const char *);
47
48
static	int	 md_cond_head(struct roff_node *);
49
static	int	 md_cond_body(struct roff_node *);
50
51
static	int	 md_pre_raw(struct roff_node *);
52
static	int	 md_pre_word(struct roff_node *);
53
static	int	 md_pre_skip(struct roff_node *);
54
static	void	 md_pre_syn(struct roff_node *);
55
static	int	 md_pre_An(struct roff_node *);
56
static	int	 md_pre_Ap(struct roff_node *);
57
static	int	 md_pre_Bd(struct roff_node *);
58
static	int	 md_pre_Bk(struct roff_node *);
59
static	int	 md_pre_Bl(struct roff_node *);
60
static	int	 md_pre_D1(struct roff_node *);
61
static	int	 md_pre_Dl(struct roff_node *);
62
static	int	 md_pre_En(struct roff_node *);
63
static	int	 md_pre_Eo(struct roff_node *);
64
static	int	 md_pre_Fa(struct roff_node *);
65
static	int	 md_pre_Fd(struct roff_node *);
66
static	int	 md_pre_Fn(struct roff_node *);
67
static	int	 md_pre_Fo(struct roff_node *);
68
static	int	 md_pre_In(struct roff_node *);
69
static	int	 md_pre_It(struct roff_node *);
70
static	int	 md_pre_Lk(struct roff_node *);
71
static	int	 md_pre_Mt(struct roff_node *);
72
static	int	 md_pre_Nd(struct roff_node *);
73
static	int	 md_pre_Nm(struct roff_node *);
74
static	int	 md_pre_No(struct roff_node *);
75
static	int	 md_pre_Ns(struct roff_node *);
76
static	int	 md_pre_Pp(struct roff_node *);
77
static	int	 md_pre_Rs(struct roff_node *);
78
static	int	 md_pre_Sh(struct roff_node *);
79
static	int	 md_pre_Sm(struct roff_node *);
80
static	int	 md_pre_Vt(struct roff_node *);
81
static	int	 md_pre_Xr(struct roff_node *);
82
static	int	 md_pre__T(struct roff_node *);
83
static	int	 md_pre_br(struct roff_node *);
84
85
static	void	 md_post_raw(struct roff_node *);
86
static	void	 md_post_word(struct roff_node *);
87
static	void	 md_post_pc(struct roff_node *);
88
static	void	 md_post_Bk(struct roff_node *);
89
static	void	 md_post_Bl(struct roff_node *);
90
static	void	 md_post_D1(struct roff_node *);
91
static	void	 md_post_En(struct roff_node *);
92
static	void	 md_post_Eo(struct roff_node *);
93
static	void	 md_post_Fa(struct roff_node *);
94
static	void	 md_post_Fd(struct roff_node *);
95
static	void	 md_post_Fl(struct roff_node *);
96
static	void	 md_post_Fn(struct roff_node *);
97
static	void	 md_post_Fo(struct roff_node *);
98
static	void	 md_post_In(struct roff_node *);
99
static	void	 md_post_It(struct roff_node *);
100
static	void	 md_post_Lb(struct roff_node *);
101
static	void	 md_post_Nm(struct roff_node *);
102
static	void	 md_post_Pf(struct roff_node *);
103
static	void	 md_post_Vt(struct roff_node *);
104
static	void	 md_post__T(struct roff_node *);
105
106
static	const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
107
	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
108
	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
109
	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
110
	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
111
	{ NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
112
	{ NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
113
	{ md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
114
	{ md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
115
	{ md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
116
	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
117
	{ md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
118
	{ NULL, NULL, NULL, NULL, NULL }, /* El */
119
	{ NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
120
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
121
	{ NULL, md_pre_An, NULL, NULL, NULL }, /* An */
122
	{ NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
123
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
124
	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
125
	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
126
	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
127
	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
128
	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
129
	{ NULL, NULL, NULL, NULL, NULL }, /* Ex */
130
	{ NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
131
	{ NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
132
	{ NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
133
	{ NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
134
	{ NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
135
	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
136
	{ NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
137
	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
138
	{ md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
139
	{ NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
140
	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
141
	{ NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */
142
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
143
	{ NULL, NULL, NULL, NULL, NULL }, /* Rv */
144
	{ NULL, NULL, NULL, NULL, NULL }, /* St */
145
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
146
	{ NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
147
	{ NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
148
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
149
	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
150
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
151
	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
152
	{ NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
153
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
154
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
155
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
156
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
157
	{ NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
158
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
159
	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
160
	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
161
	{ md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
162
	{ NULL, NULL, NULL, NULL, NULL }, /* At */
163
	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
164
	{ NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
165
	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
166
	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
167
	{ NULL, NULL, NULL, NULL, NULL }, /* Bsx */
168
	{ NULL, NULL, NULL, NULL, NULL }, /* Bx */
169
	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
170
	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
171
	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
172
	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
173
	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
174
	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
175
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
176
	{ md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
177
	{ NULL, NULL, NULL, NULL, NULL }, /* Fx */
178
	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
179
	{ NULL, md_pre_No, NULL, NULL, NULL }, /* No */
180
	{ NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
181
	{ NULL, NULL, NULL, NULL, NULL }, /* Nx */
182
	{ NULL, NULL, NULL, NULL, NULL }, /* Ox */
183
	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
184
	{ NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
185
	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
186
	{ md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
187
	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
188
	{ md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
189
	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
190
	{ md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
191
	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
192
	{ md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
193
	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
194
	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
195
	{ md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
196
	{ NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
197
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
198
	{ NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
199
	{ NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
200
	{ NULL, NULL, NULL, NULL, NULL }, /* Ux */
201
	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
202
	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
203
	{ NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
204
	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
205
	{ md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
206
	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
207
	{ NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
208
	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
209
	{ NULL, NULL, NULL, NULL, NULL }, /* Bt */
210
	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
211
	{ NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
212
	{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
213
	{ NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
214
	{ NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
215
	{ NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
216
	{ NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
217
	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
218
	{ md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
219
	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
220
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
221
	{ NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
222
	{ md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
223
	{ NULL, NULL, NULL, NULL, NULL }, /* Dx */
224
	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
225
	{ NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
226
	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
227
};
228
static	const struct md_act *const md_acts = __md_acts - MDOC_Dd;
229
230
static	int	 outflags;
231
#define	MD_spc		 (1 << 0)  /* Blank character before next word. */
232
#define	MD_spc_force	 (1 << 1)  /* Even before trailing punctuation. */
233
#define	MD_nonl		 (1 << 2)  /* Prevent linebreak in markdown code. */
234
#define	MD_nl		 (1 << 3)  /* Break markdown code line. */
235
#define	MD_br		 (1 << 4)  /* Insert an output line break. */
236
#define	MD_sp		 (1 << 5)  /* Insert a paragraph break. */
237
#define	MD_Sm		 (1 << 6)  /* Horizontal spacing mode. */
238
#define	MD_Bk		 (1 << 7)  /* Word keep mode. */
239
#define	MD_An_split	 (1 << 8)  /* Author mode is "split". */
240
#define	MD_An_nosplit	 (1 << 9)  /* Author mode is "nosplit". */
241
242
static	int	 escflags; /* Escape in generated markdown code: */
243
#define	ESC_BOL	 (1 << 0)  /* "#*+-" near the beginning of a line. */
244
#define	ESC_NUM	 (1 << 1)  /* "." after a leading number. */
245
#define	ESC_HYP	 (1 << 2)  /* "(" immediately after "]". */
246
#define	ESC_SQU	 (1 << 4)  /* "]" when "[" is open. */
247
#define	ESC_FON	 (1 << 5)  /* "*" immediately after unrelated "*". */
248
#define	ESC_EOL	 (1 << 6)  /* " " at the and of a line. */
249
250
static	int	 code_blocks, quote_blocks, list_blocks;
251
static	int	 outcount;
252
253
void
254
markdown_mdoc(void *arg, const struct roff_man *mdoc)
255
{
256
3924
	outflags = MD_Sm;
257
1962
	md_word(mdoc->meta.title);
258
1962
	if (mdoc->meta.msec != NULL) {
259
1926
		outflags &= ~MD_spc;
260
1926
		md_word("(");
261
1926
		md_word(mdoc->meta.msec);
262
1926
		md_word(")");
263
1926
	}
264
1962
	md_word("-");
265
1962
	md_word(mdoc->meta.vol);
266
1962
	if (mdoc->meta.arch != NULL) {
267
9
		md_word("(");
268
9
		md_word(mdoc->meta.arch);
269
9
		md_word(")");
270
9
	}
271
1962
	outflags |= MD_sp;
272
273
1962
	md_nodelist(mdoc->first->child);
274
275
1962
	outflags |= MD_sp;
276
1962
	md_word(mdoc->meta.os);
277
1962
	md_word("-");
278
1962
	md_word(mdoc->meta.date);
279
3924
	putchar('\n');
280
1962
}
281
282
static void
283
md_nodelist(struct roff_node *n)
284
{
285
359028
	while (n != NULL) {
286
106155
		md_node(n);
287
106155
		n = n->next;
288
	}
289
48906
}
290
291
static void
292
md_node(struct roff_node *n)
293
{
294
	const struct md_act	*act;
295
	int			 cond, process_children;
296
297
215604
	if (n->flags & NODE_NOPRT)
298
5958
		return;
299
300
101844
	if (outflags & MD_nonl)
301
2124
		outflags &= ~(MD_nl | MD_sp);
302

182601
	else if (outflags & MD_spc && n->flags & NODE_LINE)
303
35640
		outflags |= MD_nl;
304
305
	act = NULL;
306
	cond = 0;
307
	process_children = 1;
308
101844
	n->flags &= ~NODE_ENDED;
309
310
101844
	if (n->type == ROFFT_TEXT) {
311
41589
		if (n->flags & NODE_DELIMC)
312
2268
			outflags &= ~(MD_spc | MD_spc_force);
313
39321
		else if (outflags & MD_Sm)
314
38844
			outflags |= MD_spc_force;
315
82701
		md_word(n->string);
316
41589
		if (n->flags & NODE_DELIMO)
317
792
			outflags &= ~(MD_spc | MD_spc_force);
318
40797
		else if (outflags & MD_Sm)
319
40311
			outflags |= MD_spc;
320
60255
	} else if (n->tok < ROFF_MAX) {
321
126
		switch (n->tok) {
322
		case ROFF_br:
323
117
			process_children = md_pre_br(n);
324
117
			break;
325
		case ROFF_sp:
326
9
			process_children = md_pre_Pp(n);
327
9
			break;
328
		default:
329
			process_children = 0;
330
			break;
331
		}
332
	} else {
333

120258
		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
334
60129
		act = md_acts + n->tok;
335
140553
		cond = act->cond == NULL || (*act->cond)(n);
336

107325
		if (cond && act->pre != NULL &&
337
46098
		    (n->end == ENDBODY_NOT || n->child != NULL))
338
45342
			process_children = (*act->pre)(n);
339
	}
340
341

198270
	if (process_children && n->child != NULL)
342
46944
		md_nodelist(n->child);
343
344
101844
	if (n->flags & NODE_ENDED)
345
387
		return;
346
347

147879
	if (cond && act->post != NULL)
348
26667
		(*act->post)(n);
349
350
101457
	if (n->end != ENDBODY_NOT)
351
387
		n->body->flags |= NODE_ENDED;
352
209259
}
353
354
static const char *
355
md_stack(char c)
356
{
357
	static char	*stack;
358
	static size_t	 sz;
359
	static size_t	 cur;
360
361
76932
	switch (c) {
362
	case '\0':
363
		break;
364
	case (char)-1:
365
2718
		assert(cur);
366
2718
		stack[--cur] = '\0';
367
2718
		break;
368
	default:
369
2718
		if (cur + 1 >= sz) {
370
396
			sz += 8;
371
396
			stack = mandoc_realloc(stack, sz);
372
396
		}
373
2718
		stack[cur] = c;
374
2718
		stack[++cur] = '\0';
375
2718
		break;
376
	}
377
41184
	return stack == NULL ? "" : stack;
378
}
379
380
/*
381
 * Handle vertical and horizontal spacing.
382
 */
383
static void
384
md_preword(void)
385
{
386
	const char	*cp;
387
388
	/*
389
	 * If a list block is nested inside a code block or a blockquote,
390
	 * blank lines for paragraph breaks no longer work; instead,
391
	 * they terminate the list.  Work around this markdown issue
392
	 * by using mere line breaks instead.
393
	 */
394
395

189900
	if (list_blocks && outflags & MD_sp) {
396
		outflags &= ~MD_sp;
397
		outflags |= MD_br;
398
	}
399
400
	/*
401
	 * End the old line if requested.
402
	 * Escape whitespace at the end of the markdown line
403
	 * such that it won't look like an output line break.
404
	 */
405
406
94950
	if (outflags & MD_sp)
407
33948
		putchar('\n');
408
77976
	else if (outflags & MD_br) {
409
2016
		putchar(' ');
410
2016
		putchar(' ');
411

89298
	} else if (outflags & MD_nl && escflags & ESC_EOL)
412
18
		md_named("zwnj");
413
414
	/* Start a new line if necessary. */
415
416
94950
	if (outflags & (MD_nl | MD_br | MD_sp)) {
417
60624
		putchar('\n');
418
66132
		for (cp = md_stack('\0'); *cp != '\0'; cp++) {
419
5508
			putchar(*cp);
420
2754
			if (*cp == '>')
421
3762
				putchar(' ');
422
		}
423
30312
		outflags &= ~(MD_nl | MD_br | MD_sp);
424
30312
		escflags = ESC_BOL;
425
		outcount = 0;
426
427
	/* Handle horizontal spacing. */
428
429
94950
	} else if (outflags & MD_spc) {
430
22446
		if (outflags & MD_Bk)
431
126
			fputs("&nbsp;", stdout);
432
		else
433
44640
			putchar(' ');
434
22446
		escflags &= ~ESC_FON;
435
22446
		outcount++;
436
22446
	}
437
438
147708
	outflags &= ~(MD_spc_force | MD_nonl);
439
189900
	if (outflags & MD_Sm)
440
94950
		outflags |= MD_spc;
441
	else
442
94950
		outflags &= ~MD_spc;
443
94950
}
444
445
/*
446
 * Print markdown syntax elements.
447
 * Can also be used for constant strings when neither escaping
448
 * nor delimiter handling is required.
449
 */
450
static void
451
md_rawword(const char *s)
452
{
453
54702
	md_preword();
454
455
27351
	if (*s == '\0')
456
		return;
457
458
27351
	if (escflags & ESC_FON) {
459
153
		escflags &= ~ESC_FON;
460
153
		if (*s == '*' && !code_blocks)
461
153
			fputs("&zwnj;", stdout);
462
	}
463
464
110097
	while (*s != '\0') {
465

73710
		switch(*s) {
466
		case '*':
467
31995
			if (s[1] == '\0')
468
19134
				escflags |= ESC_FON;
469
			break;
470
		case '[':
471
171
			escflags |= ESC_SQU;
472
171
			break;
473
		case ']':
474
171
			escflags |= ESC_HYP;
475
171
			escflags &= ~ESC_SQU;
476
171
			break;
477
		default:
478
			break;
479
		}
480
41373
		md_char(*s++);
481
	}
482
54702
	if (s[-1] == ' ')
483
27351
		escflags |= ESC_EOL;
484
	else
485
27351
		escflags &= ~ESC_EOL;
486
54702
}
487
488
/*
489
 * Print text and mdoc(7) syntax elements.
490
 */
491
static void
492
md_word(const char *s)
493
{
494
67302
	const char	*seq, *prevfont, *currfont, *nextfont;
495
	char		 c;
496
67302
	int		 bs, sz, uc, breakline;
497
498
	/* No spacing before closing delimiters. */
499

142929
	if (s[0] != '\0' && s[1] == '\0' &&
500
28701
	    strchr("!),.:;?]", s[0]) != NULL &&
501
8397
	    (outflags & MD_spc_force) == 0)
502
6966
		outflags &= ~MD_spc;
503
504
67302
	md_preword();
505
506
67302
	if (*s == '\0')
507
72
		return;
508
509
	/* No spacing after opening delimiters. */
510

135774
	if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
511
4788
		outflags &= ~MD_spc;
512
513
	breakline = 0;
514
	prevfont = currfont = "";
515
694008
	while ((c = *s++) != '\0') {
516
		bs = 0;
517




1209663
		switch(c) {
518
		case ASCII_NBRSP:
519
			if (code_blocks)
520
				c = ' ';
521
			else {
522
				md_named("nbsp");
523
				c = '\0';
524
			}
525
			break;
526
		case ASCII_HYPH:
527
342
			bs = escflags & ESC_BOL && !code_blocks;
528
			c = '-';
529
171
			break;
530
		case ASCII_BREAK:
531
			continue;
532
		case '#':
533
		case '+':
534
		case '-':
535
20970
			bs = escflags & ESC_BOL && !code_blocks;
536
10476
			break;
537
		case '(':
538
7281
			bs = escflags & ESC_HYP && !code_blocks;
539
3627
			break;
540
		case ')':
541
7902
			bs = escflags & ESC_NUM && !code_blocks;
542
3951
			break;
543
		case '*':
544
		case '[':
545
		case '_':
546
		case '`':
547
2439
			bs = !code_blocks;
548
2439
			break;
549
		case '.':
550
6039
			bs = escflags & ESC_NUM && !code_blocks;
551
2988
			break;
552
		case '<':
553
729
			if (code_blocks == 0) {
554
720
				md_named("lt");
555
				c = '\0';
556
720
			}
557
			break;
558
		case '=':
559
36
			if (escflags & ESC_BOL && !code_blocks) {
560
				md_named("equals");
561
				c = '\0';
562
			}
563
			break;
564
		case '>':
565
720
			if (code_blocks == 0) {
566
711
				md_named("gt");
567
				c = '\0';
568
711
			}
569
			break;
570
		case '\\':
571
			uc = 0;
572
			nextfont = NULL;
573


2070
			switch (mandoc_escape(&s, &seq, &sz)) {
574
			case ESCAPE_UNICODE:
575
				uc = mchars_num2uc(seq + 1, sz - 1);
576
				break;
577
			case ESCAPE_NUMBERED:
578
				uc = mchars_num2char(seq, sz);
579
				break;
580
			case ESCAPE_SPECIAL:
581
585
				uc = mchars_spec2cp(seq, sz);
582
585
				break;
583
			case ESCAPE_FONTBOLD:
584
				nextfont = "**";
585
90
				break;
586
			case ESCAPE_FONTITALIC:
587
				nextfont = "*";
588
135
				break;
589
			case ESCAPE_FONTBI:
590
				nextfont = "***";
591
				break;
592
			case ESCAPE_FONT:
593
			case ESCAPE_FONTROMAN:
594
				nextfont = "";
595
				break;
596
			case ESCAPE_FONTPREV:
597
				nextfont = prevfont;
598
225
				break;
599
			case ESCAPE_BREAK:
600
				breakline = 1;
601
				break;
602
			case ESCAPE_NOSPACE:
603
			case ESCAPE_SKIPCHAR:
604
			case ESCAPE_OVERSTRIKE:
605
				/* XXX not implemented */
606
				/* FALLTHROUGH */
607
			case ESCAPE_ERROR:
608
			default:
609
				break;
610
			}
611
1035
			if (nextfont != NULL && !code_blocks) {
612
360
				if (*currfont != '\0') {
613
180
					outflags &= ~MD_spc;
614
180
					md_rawword(currfont);
615
180
				}
616
				prevfont = currfont;
617
				currfont = nextfont;
618
360
				if (*currfont != '\0') {
619
180
					outflags &= ~MD_spc;
620
180
					md_rawword(currfont);
621
180
				}
622
			}
623
1035
			if (uc) {
624
477
				if ((uc < 0x20 && uc != 0x09) ||
625
477
				    (uc > 0x7E && uc < 0xA0))
626
					uc = 0xFFFD;
627
477
				if (code_blocks) {
628
					seq = mchars_uc2str(uc);
629
					fputs(seq, stdout);
630
					outcount += strlen(seq);
631
				} else {
632
477
					printf("&#%d;", uc);
633
477
					outcount++;
634
				}
635
477
				escflags &= ~ESC_FON;
636
477
			}
637
			c = '\0';
638
1035
			break;
639
		case ']':
640
2628
			bs = escflags & ESC_SQU && !code_blocks;
641
1314
			escflags |= ESC_HYP;
642
1314
			break;
643
		default:
644
			break;
645
		}
646
559548
		if (bs)
647
5004
			putchar('\\');
648
559548
		md_char(c);
649

559548
		if (breakline &&
650
		    (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
651
			printf("  \n");
652
			breakline = 0;
653
			while (*s == ' ' || *s == ASCII_NBRSP)
654
				s++;
655
		}
656
	}
657
67230
	if (*currfont != '\0') {
658
		outflags &= ~MD_spc;
659
		md_rawword(currfont);
660
134460
	} else if (s[-2] == ' ')
661
67230
		escflags |= ESC_EOL;
662
	else
663
67230
		escflags &= ~ESC_EOL;
664
134532
}
665
666
/*
667
 * Print a single HTML named character reference.
668
 */
669
static void
670
md_named(const char *s)
671
{
672
2898
	printf("&%s;", s);
673
1449
	escflags &= ~(ESC_FON | ESC_EOL);
674
1449
	outcount++;
675
1449
}
676
677
/*
678
 * Print a single raw character and maintain certain escape flags.
679
 */
680
static void
681
md_char(unsigned char c)
682
{
683
1201842
	if (c != '\0') {
684
1196910
		putchar(c);
685
1196910
		if (c == '*')
686
598455
			escflags |= ESC_FON;
687
		else
688
598455
			escflags &= ~ESC_FON;
689
598455
		outcount++;
690
598455
	}
691
600921
	if (c != ']')
692
599436
		escflags &= ~ESC_HYP;
693

1634049
	if (c == ' ' || c == '\t' || c == '>')
694
		return;
695
516078
	if (isdigit(c) == 0)
696
500274
		escflags &= ~ESC_NUM;
697
15804
	else if (escflags & ESC_BOL)
698
153
		escflags |= ESC_NUM;
699
1016505
	escflags &= ~ESC_BOL;
700
1116999
}
701
702
static int
703
md_cond_head(struct roff_node *n)
704
{
705
11610
	return n->type == ROFFT_HEAD;
706
}
707
708
static int
709
md_cond_body(struct roff_node *n)
710
{
711
28980
	return n->type == ROFFT_BODY;
712
}
713
714
static int
715
md_pre_raw(struct roff_node *n)
716
{
717
	const char	*prefix;
718
719
18216
	if ((prefix = md_acts[n->tok].prefix) != NULL) {
720
9108
		md_rawword(prefix);
721
9108
		outflags &= ~MD_spc;
722
9108
		if (*prefix == '`')
723
900
			code_blocks++;
724
	}
725
9108
	return 1;
726
}
727
728
static void
729
md_post_raw(struct roff_node *n)
730
{
731
	const char	*suffix;
732
733
18828
	if ((suffix = md_acts[n->tok].suffix) != NULL) {
734
9108
		outflags &= ~(MD_spc | MD_nl);
735
9108
		md_rawword(suffix);
736
9108
		if (*suffix == '`')
737
909
			code_blocks--;
738
	}
739
9414
}
740
741
static int
742
md_pre_word(struct roff_node *n)
743
{
744
	const char	*prefix;
745
746
3240
	if ((prefix = md_acts[n->tok].prefix) != NULL) {
747
1620
		md_word(prefix);
748
1620
		outflags &= ~MD_spc;
749
1620
	}
750
1620
	return 1;
751
}
752
753
static void
754
md_post_word(struct roff_node *n)
755
{
756
	const char	*suffix;
757
758
3240
	if ((suffix = md_acts[n->tok].suffix) != NULL) {
759
1620
		outflags &= ~(MD_spc | MD_nl);
760
1620
		md_word(suffix);
761
1620
	}
762
1620
}
763
764
static void
765
md_post_pc(struct roff_node *n)
766
{
767
810
	md_post_raw(n);
768
405
	if (n->parent->tok != MDOC_Rs)
769
		return;
770
405
	if (n->next != NULL) {
771
333
		md_word(",");
772

351
		if (n->prev != NULL &&
773
279
		    n->prev->tok == n->tok &&
774
18
		    n->next->tok == n->tok)
775
9
			md_word("and");
776
	} else {
777
72
		md_word(".");
778
72
		outflags |= MD_nl;
779
	}
780
405
}
781
782
static int
783
md_pre_skip(struct roff_node *n)
784
{
785
36
	return 0;
786
}
787
788
static void
789
md_pre_syn(struct roff_node *n)
790
{
791

4689
	if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
792
		return;
793
794

513
	if (n->prev->tok == n->tok &&
795
54
	    n->tok != MDOC_Ft &&
796
54
	    n->tok != MDOC_Fo &&
797
54
	    n->tok != MDOC_Fn) {
798
54
		outflags |= MD_br;
799
54
		return;
800
	}
801
802

603
	switch (n->prev->tok) {
803
	case MDOC_Fd:
804
	case MDOC_Fn:
805
	case MDOC_Fo:
806
	case MDOC_In:
807
	case MDOC_Vt:
808
189
		outflags |= MD_sp;
809
189
		break;
810
	case MDOC_Ft:
811

261
		if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
812
			outflags |= MD_sp;
813
			break;
814
		}
815
		/* FALLTHROUGH */
816
	default:
817
216
		outflags |= MD_br;
818
216
		break;
819
	}
820
1674
}
821
822
static int
823
md_pre_An(struct roff_node *n)
824
{
825
522
	switch (n->norm->An.auth) {
826
	case AUTH_split:
827
9
		outflags &= ~MD_An_nosplit;
828
9
		outflags |= MD_An_split;
829
9
		return 0;
830
	case AUTH_nosplit:
831
18
		outflags &= ~MD_An_split;
832
18
		outflags |= MD_An_nosplit;
833
18
		return 0;
834
	default:
835
234
		if (outflags & MD_An_split)
836
90
			outflags |= MD_br;
837

207
		else if (n->sec == SEC_AUTHORS &&
838
63
		    ! (outflags & MD_An_nosplit))
839
18
			outflags |= MD_An_split;
840
234
		return 1;
841
	}
842
261
}
843
844
static int
845
md_pre_Ap(struct roff_node *n)
846
{
847
72
	outflags &= ~MD_spc;
848
36
	md_word("'");
849
36
	outflags &= ~MD_spc;
850
36
	return 0;
851
}
852
853
static int
854
md_pre_Bd(struct roff_node *n)
855
{
856
846
	switch (n->norm->Bd.type) {
857
	case DISP_unfilled:
858
	case DISP_literal:
859
144
		return md_pre_Dl(n);
860
	default:
861
279
		return md_pre_D1(n);
862
	}
863
423
}
864
865
static int
866
md_pre_Bk(struct roff_node *n)
867
{
868
108
	switch (n->type) {
869
	case ROFFT_BLOCK:
870
18
		return 1;
871
	case ROFFT_BODY:
872
18
		outflags |= MD_Bk;
873
18
		return 1;
874
	default:
875
18
		return 0;
876
	}
877
54
}
878
879
static void
880
md_post_Bk(struct roff_node *n)
881
{
882
108
	if (n->type == ROFFT_BODY)
883
18
		outflags &= ~MD_Bk;
884
54
}
885
886
static int
887
md_pre_Bl(struct roff_node *n)
888
{
889
4464
	n->norm->Bl.count = 0;
890
2232
	if (n->norm->Bl.type == LIST_column)
891
189
		md_pre_Dl(n);
892
2232
	outflags |= MD_sp;
893
2232
	return 1;
894
}
895
896
static void
897
md_post_Bl(struct roff_node *n)
898
{
899
4464
	n->norm->Bl.count = 0;
900
2232
	if (n->norm->Bl.type == LIST_column)
901
189
		md_post_D1(n);
902
2232
	outflags |= MD_sp;
903
2232
}
904
905
static int
906
md_pre_D1(struct roff_node *n)
907
{
908
	/*
909
	 * Markdown blockquote syntax does not work inside code blocks.
910
	 * The best we can do is fall back to another nested code block.
911
	 */
912
2646
	if (code_blocks) {
913
		md_stack('\t');
914
		code_blocks++;
915
	} else {
916
1323
		md_stack('>');
917
		quote_blocks++;
918
	}
919
1323
	outflags |= MD_sp;
920
1323
	return 1;
921
}
922
923
static void
924
md_post_D1(struct roff_node *n)
925
{
926
3384
	md_stack((char)-1);
927
1692
	if (code_blocks)
928
		code_blocks--;
929
	else
930
		quote_blocks--;
931
1692
	outflags |= MD_sp;
932
1692
}
933
934
static int
935
md_pre_Dl(struct roff_node *n)
936
{
937
	/*
938
	 * Markdown code block syntax does not work inside blockquotes.
939
	 * The best we can do is fall back to another nested blockquote.
940
	 */
941
738
	if (quote_blocks) {
942
9
		md_stack('>');
943
		quote_blocks++;
944
9
	} else {
945
360
		md_stack('\t');
946
		code_blocks++;
947
	}
948
369
	outflags |= MD_sp;
949
369
	return 1;
950
}
951
952
static int
953
md_pre_En(struct roff_node *n)
954
{
955

99
	if (n->norm->Es == NULL ||
956
27
	    n->norm->Es->child == NULL)
957
18
		return 1;
958
959
18
	md_word(n->norm->Es->child->string);
960
18
	outflags &= ~MD_spc;
961
18
	return 1;
962
36
}
963
964
static void
965
md_post_En(struct roff_node *n)
966
{
967

90
	if (n->norm->Es == NULL ||
968
27
	    n->norm->Es->child == NULL ||
969
18
	    n->norm->Es->child->next == NULL)
970
		return;
971
972
18
	outflags &= ~MD_spc;
973
18
	md_word(n->norm->Es->child->next->string);
974
54
}
975
976
static int
977
md_pre_Eo(struct roff_node *n)
978
{
979

459
	if (n->end == ENDBODY_NOT &&
980
189
	    n->parent->head->child == NULL &&
981
81
	    n->child != NULL &&
982
45
	    n->child->end != ENDBODY_NOT)
983
9
		md_preword();
984

234
	else if (n->end != ENDBODY_NOT ? n->child != NULL :
985

288
	    n->parent->head->child != NULL && (n->child != NULL ||
986
45
	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
987
108
		outflags &= ~(MD_spc | MD_nl);
988
207
	return 1;
989
}
990
991
static void
992
md_post_Eo(struct roff_node *n)
993
{
994
378
	if (n->end != ENDBODY_NOT) {
995
36
		outflags |= MD_spc;
996
36
		return;
997
	}
998
999

216
	if (n->child == NULL && n->parent->head->child == NULL)
1000
		return;
1001
1002

225
	if (n->parent->tail != NULL && n->parent->tail->child != NULL)
1003
72
		outflags &= ~MD_spc;
1004
        else
1005
45
		outflags |= MD_spc;
1006
342
}
1007
1008
static int
1009
md_pre_Fa(struct roff_node *n)
1010
{
1011
	int	 am_Fa;
1012
1013
1350
	am_Fa = n->tok == MDOC_Fa;
1014
1015
675
	if (am_Fa)
1016
189
		n = n->child;
1017
1018
1575
	while (n != NULL) {
1019
900
		md_rawword("*");
1020
900
		outflags &= ~MD_spc;
1021
900
		md_node(n);
1022
900
		outflags &= ~MD_spc;
1023
900
		md_rawword("*");
1024
900
		if ((n = n->next) != NULL)
1025
225
			md_word(",");
1026
	}
1027
675
	return 0;
1028
}
1029
1030
static void
1031
md_post_Fa(struct roff_node *n)
1032
{
1033

432
	if (n->next != NULL && n->next->tok == MDOC_Fa)
1034
36
		md_word(",");
1035
189
}
1036
1037
static int
1038
md_pre_Fd(struct roff_node *n)
1039
{
1040
1476
	md_pre_syn(n);
1041
738
	md_pre_raw(n);
1042
738
	return 1;
1043
}
1044
1045
static void
1046
md_post_Fd(struct roff_node *n)
1047
{
1048
198
	md_post_raw(n);
1049
99
	outflags |= MD_br;
1050
99
}
1051
1052
static void
1053
md_post_Fl(struct roff_node *n)
1054
{
1055
1476
	md_post_raw(n);
1056

945
	if (n->child == NULL && n->next != NULL &&
1057
207
	    n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
1058
36
		outflags &= ~MD_spc;
1059
738
}
1060
1061
static int
1062
md_pre_Fn(struct roff_node *n)
1063
{
1064
1134
	md_pre_syn(n);
1065
1066
567
	if ((n = n->child) == NULL)
1067
		return 0;
1068
1069
567
	md_rawword("**");
1070
567
	outflags &= ~MD_spc;
1071
567
	md_node(n);
1072
567
	outflags &= ~MD_spc;
1073
567
	md_rawword("**");
1074
567
	outflags &= ~MD_spc;
1075
567
	md_word("(");
1076
1077
567
	if ((n = n->next) != NULL)
1078
486
		md_pre_Fa(n);
1079
567
	return 0;
1080
567
}
1081
1082
static void
1083
md_post_Fn(struct roff_node *n)
1084
{
1085
1494
	md_word(")");
1086
747
	if (n->flags & NODE_SYNPRETTY) {
1087
198
		md_word(";");
1088
198
		outflags |= MD_sp;
1089
198
	}
1090
747
}
1091
1092
static int
1093
md_pre_Fo(struct roff_node *n)
1094
{
1095

1611
	switch (n->type) {
1096
	case ROFFT_BLOCK:
1097
180
		md_pre_syn(n);
1098
180
		break;
1099
	case ROFFT_HEAD:
1100
180
		if (n->child == NULL)
1101
9
			return 0;
1102
171
		md_pre_raw(n);
1103
171
		break;
1104
	case ROFFT_BODY:
1105
180
		outflags &= ~(MD_spc | MD_nl);
1106
180
		md_word("(");
1107
180
		break;
1108
	default:
1109
		break;
1110
	}
1111
531
	return 1;
1112
540
}
1113
1114
static void
1115
md_post_Fo(struct roff_node *n)
1116
{
1117
1440
	switch (n->type) {
1118
	case ROFFT_HEAD:
1119
180
		if (n->child != NULL)
1120
171
			md_post_raw(n);
1121
		break;
1122
	case ROFFT_BODY:
1123
180
		md_post_Fn(n);
1124
180
		break;
1125
	default:
1126
		break;
1127
	}
1128
540
}
1129
1130
static int
1131
md_pre_In(struct roff_node *n)
1132
{
1133
216
	if (n->flags & NODE_SYNPRETTY) {
1134
54
		md_pre_syn(n);
1135
54
		md_rawword("**");
1136
54
		outflags &= ~MD_spc;
1137
54
		md_word("#include <");
1138
54
	} else {
1139
54
		md_word("<");
1140
54
		outflags &= ~MD_spc;
1141
54
		md_rawword("*");
1142
	}
1143
108
	outflags &= ~MD_spc;
1144
108
	return 1;
1145
}
1146
1147
static void
1148
md_post_In(struct roff_node *n)
1149
{
1150
216
	if (n->flags & NODE_SYNPRETTY) {
1151
		outflags &= ~MD_spc;
1152
54
		md_rawword(">**");
1153
54
		outflags |= MD_nl;
1154
54
	} else {
1155
		outflags &= ~MD_spc;
1156
54
		md_rawword("*>");
1157
	}
1158
108
}
1159
1160
static int
1161
md_pre_It(struct roff_node *n)
1162
{
1163
	struct roff_node	*bln;
1164
1165

22014
	switch (n->type) {
1166
	case ROFFT_BLOCK:
1167
3348
		return 1;
1168
1169
	case ROFFT_HEAD:
1170
3348
		bln = n->parent->parent;
1171

6048
		if (bln->norm->Bl.comp == 0 &&
1172
2700
		    bln->norm->Bl.type != LIST_column)
1173
2223
			outflags |= MD_sp;
1174
3348
		outflags |= MD_nl;
1175
1176



3348
		switch (bln->norm->Bl.type) {
1177
		case LIST_item:
1178
306
			outflags |= MD_br;
1179
306
			return 0;
1180
		case LIST_inset:
1181
		case LIST_diag:
1182
		case LIST_ohang:
1183
504
			outflags |= MD_br;
1184
504
			return 1;
1185
		case LIST_tag:
1186
		case LIST_hang:
1187
1026
			outflags |= MD_sp;
1188
1026
			return 1;
1189
		case LIST_bullet:
1190
351
			md_rawword("*\t");
1191
351
			break;
1192
		case LIST_dash:
1193
		case LIST_hyphen:
1194
387
			md_rawword("-\t");
1195
387
			break;
1196
		case LIST_enum:
1197
288
			md_preword();
1198
288
			if (bln->norm->Bl.count < 99)
1199
288
				bln->norm->Bl.count++;
1200
288
			printf("%d.\t", bln->norm->Bl.count);
1201
288
			escflags &= ~ESC_FON;
1202
288
			break;
1203
		case LIST_column:
1204
486
			outflags |= MD_br;
1205
486
			return 0;
1206
		default:
1207
			return 0;
1208
		}
1209
1026
		outflags &= ~MD_spc;
1210
1026
		outflags |= MD_nonl;
1211
1026
		outcount = 0;
1212
1026
		md_stack('\t');
1213
1026
		if (code_blocks || quote_blocks)
1214
			list_blocks++;
1215
1026
		return 0;
1216
1217
	case ROFFT_BODY:
1218
5427
		bln = n->parent->parent;
1219

5427
		switch (bln->norm->Bl.type) {
1220
		case LIST_ohang:
1221
90
			outflags |= MD_br;
1222
90
			break;
1223
		case LIST_tag:
1224
		case LIST_hang:
1225
1026
			md_pre_D1(n);
1226
1026
			break;
1227
		default:
1228
			break;
1229
		}
1230
4311
		return 1;
1231
1232
	default:
1233
		return 0;
1234
	}
1235
11007
}
1236
1237
static void
1238
md_post_It(struct roff_node *n)
1239
{
1240
	struct roff_node	*bln;
1241
	int			 i, nc;
1242
1243
22014
	if (n->type != ROFFT_BODY)
1244
6696
		return;
1245
1246
7812
	bln = n->parent->parent;
1247


7812
	switch (bln->norm->Bl.type) {
1248
	case LIST_bullet:
1249
	case LIST_dash:
1250
	case LIST_hyphen:
1251
	case LIST_enum:
1252
1026
		md_stack((char)-1);
1253
1026
		if (code_blocks || quote_blocks)
1254
			list_blocks--;
1255
		break;
1256
	case LIST_tag:
1257
	case LIST_hang:
1258
1026
		md_post_D1(n);
1259
1026
		break;
1260
1261
	case LIST_column:
1262
1449
		if (n->next == NULL)
1263
			break;
1264
1265
		/* Calculate the array index of the current column. */
1266
1267
		i = 0;
1268

5049
		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1269
720
			i++;
1270
1271
		/*
1272
		 * If a width was specified for this column,
1273
		 * subtract what printed, and
1274
		 * add the same spacing as in mdoc_term.c.
1275
		 */
1276
1277
963
		nc = bln->norm->Bl.ncols;
1278
3798
		i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1279
1980
		    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1280
963
		if (i < 1)
1281
			i = 1;
1282
7839
		while (i-- > 0)
1283
11826
			putchar(' ');
1284
1285
963
		outflags &= ~MD_spc;
1286
963
		escflags &= ~ESC_FON;
1287
963
		outcount = 0;
1288
963
		break;
1289
1290
	default:
1291
		break;
1292
	}
1293
15318
}
1294
1295
static void
1296
md_post_Lb(struct roff_node *n)
1297
{
1298
90
	if (n->sec == SEC_LIBRARY)
1299
27
		outflags |= MD_br;
1300
45
}
1301
1302
static void
1303
md_uri(const char *s)
1304
{
1305
6633
	while (*s != '\0') {
1306
3060
		if (strchr("%()<>", *s) != NULL) {
1307
			printf("%%%2.2hhX", *s);
1308
			outcount += 3;
1309
		} else {
1310
6120
			putchar(*s);
1311
			outcount++;
1312
		}
1313
3060
		s++;
1314
	}
1315
171
}
1316
1317
static int
1318
md_pre_Lk(struct roff_node *n)
1319
{
1320
	const struct roff_node *link, *descr, *punct;
1321
1322
216
	if ((link = n->child) == NULL)
1323
		return 0;
1324
1325
	/* Find beginning of trailing punctuation. */
1326
108
	punct = n->last;
1327

477
	while (punct != link && punct->flags & NODE_DELIMC)
1328
18
		punct = punct->prev;
1329
108
	punct = punct->next;
1330
1331
	/* Link text. */
1332
108
	descr = link->next;
1333
108
	if (descr == punct)
1334
27
		descr = link;  /* no text */
1335
108
	md_rawword("[");
1336
108
	outflags &= ~MD_spc;
1337
108
	do {
1338
135
		md_word(descr->string);
1339
135
		descr = descr->next;
1340
135
	} while (descr != punct);
1341
108
	outflags &= ~MD_spc;
1342
1343
	/* Link target. */
1344
108
	md_rawword("](");
1345
108
	md_uri(link->string);
1346
108
	outflags &= ~MD_spc;
1347
108
	md_rawword(")");
1348
1349
	/* Trailing punctuation. */
1350
252
	while (punct != NULL) {
1351
18
		md_word(punct->string);
1352
18
		punct = punct->next;
1353
	}
1354
108
	return 0;
1355
108
}
1356
1357
static int
1358
md_pre_Mt(struct roff_node *n)
1359
{
1360
	const struct roff_node *nch;
1361
1362
126
	md_rawword("[");
1363
63
	outflags &= ~MD_spc;
1364
252
	for (nch = n->child; nch != NULL; nch = nch->next)
1365
63
		md_word(nch->string);
1366
63
	outflags &= ~MD_spc;
1367
63
	md_rawword("](mailto:");
1368
252
	for (nch = n->child; nch != NULL; nch = nch->next) {
1369
63
		md_uri(nch->string);
1370
63
		if (nch->next != NULL) {
1371
			putchar(' ');
1372
			outcount++;
1373
		}
1374
	}
1375
63
	outflags &= ~MD_spc;
1376
63
	md_rawword(")");
1377
63
	return 0;
1378
}
1379
1380
static int
1381
md_pre_Nd(struct roff_node *n)
1382
{
1383
3870
	outflags &= ~MD_nl;
1384
1935
	outflags |= MD_spc;
1385
1935
	md_word("-");
1386
1935
	return 1;
1387
}
1388
1389
static int
1390
md_pre_Nm(struct roff_node *n)
1391
{
1392

7677
	switch (n->type) {
1393
	case ROFFT_BLOCK:
1394
108
		outflags |= MD_Bk;
1395
108
		md_pre_syn(n);
1396
108
		break;
1397
	case ROFFT_HEAD:
1398
	case ROFFT_ELEM:
1399
2385
		md_pre_raw(n);
1400
2385
		break;
1401
	default:
1402
		break;
1403
	}
1404
2592
	return 1;
1405
}
1406
1407
static void
1408
md_post_Nm(struct roff_node *n)
1409
{
1410

7677
	switch (n->type) {
1411
	case ROFFT_BLOCK:
1412
108
		outflags &= ~MD_Bk;
1413
108
		break;
1414
	case ROFFT_HEAD:
1415
	case ROFFT_ELEM:
1416
2385
		md_post_raw(n);
1417
2385
		break;
1418
	default:
1419
		break;
1420
	}
1421
2592
}
1422
1423
static int
1424
md_pre_No(struct roff_node *n)
1425
{
1426
2178
	outflags |= MD_spc_force;
1427
1089
	return 1;
1428
}
1429
1430
static int
1431
md_pre_Ns(struct roff_node *n)
1432
{
1433
522
	outflags &= ~MD_spc;
1434
261
	return 0;
1435
}
1436
1437
static void
1438
md_post_Pf(struct roff_node *n)
1439
{
1440

279
	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1441
63
		outflags &= ~MD_spc;
1442
99
}
1443
1444
static int
1445
md_pre_Pp(struct roff_node *n)
1446
{
1447
3186
	outflags |= MD_sp;
1448
1593
	return 0;
1449
}
1450
1451
static int
1452
md_pre_Rs(struct roff_node *n)
1453
{
1454
180
	if (n->sec == SEC_SEE_ALSO)
1455
54
		outflags |= MD_sp;
1456
90
	return 1;
1457
}
1458
1459
static int
1460
md_pre_Sh(struct roff_node *n)
1461
{
1462

39204
	switch (n->type) {
1463
	case ROFFT_BLOCK:
1464
4356
		if (n->sec == SEC_AUTHORS)
1465
36
			outflags &= ~(MD_An_split | MD_An_nosplit);
1466
		break;
1467
	case ROFFT_HEAD:
1468
4356
		outflags |= MD_sp;
1469
4356
		md_rawword(n->tok == MDOC_Sh ? "#" : "##");
1470
4356
		break;
1471
	case ROFFT_BODY:
1472
4356
		outflags |= MD_sp;
1473
4356
		break;
1474
	default:
1475
		break;
1476
	}
1477
13068
	return 1;
1478
}
1479
1480
static int
1481
md_pre_Sm(struct roff_node *n)
1482
{
1483
936
	if (n->child == NULL)
1484
45
		outflags ^= MD_Sm;
1485
846
	else if (strcmp("on", n->child->string) == 0)
1486
423
		outflags |= MD_Sm;
1487
	else
1488
423
		outflags &= ~MD_Sm;
1489
1490
468
	if (outflags & MD_Sm)
1491
243
		outflags |= MD_spc;
1492
1493
468
	return 0;
1494
}
1495
1496
static int
1497
md_pre_Vt(struct roff_node *n)
1498
{
1499

414
	switch (n->type) {
1500
	case ROFFT_BLOCK:
1501
27
		md_pre_syn(n);
1502
27
		return 1;
1503
	case ROFFT_BODY:
1504
	case ROFFT_ELEM:
1505
153
		md_pre_raw(n);
1506
153
		return 1;
1507
	default:
1508
27
		return 0;
1509
	}
1510
207
}
1511
1512
static void
1513
md_post_Vt(struct roff_node *n)
1514
{
1515
567
	switch (n->type) {
1516
	case ROFFT_BODY:
1517
	case ROFFT_ELEM:
1518
153
		md_post_raw(n);
1519
153
		break;
1520
	default:
1521
		break;
1522
	}
1523
207
}
1524
1525
static int
1526
md_pre_Xr(struct roff_node *n)
1527
{
1528
198
	n = n->child;
1529
99
	if (n == NULL)
1530
		return 0;
1531
99
	md_node(n);
1532
99
	n = n->next;
1533
99
	if (n == NULL)
1534
18
		return 0;
1535
81
	outflags &= ~MD_spc;
1536
81
	md_word("(");
1537
81
	md_node(n);
1538
81
	md_word(")");
1539
81
	return 0;
1540
99
}
1541
1542
static int
1543
md_pre__T(struct roff_node *n)
1544
{
1545

81
	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1546
18
		md_word("\"");
1547
	else
1548
9
		md_rawword("*");
1549
27
	outflags &= ~MD_spc;
1550
27
	return 1;
1551
}
1552
1553
static void
1554
md_post__T(struct roff_node *n)
1555
{
1556
54
	outflags &= ~MD_spc;
1557

54
	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T)
1558
18
		md_word("\"");
1559
	else
1560
9
		md_rawword("*");
1561
27
	md_post_pc(n);
1562
27
}
1563
1564
static int
1565
md_pre_br(struct roff_node *n)
1566
{
1567
234
	outflags |= MD_br;
1568
117
	return 0;
1569
}