GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_markdown.c Lines: 617 658 93.8 %
Date: 2017-11-13 Branches: 402 504 79.8 %

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
1308
	outflags = MD_Sm;
257
654
	md_word(mdoc->meta.title);
258
654
	if (mdoc->meta.msec != NULL) {
259
642
		outflags &= ~MD_spc;
260
642
		md_word("(");
261
642
		md_word(mdoc->meta.msec);
262
642
		md_word(")");
263
642
	}
264
654
	md_word("-");
265
654
	md_word(mdoc->meta.vol);
266
654
	if (mdoc->meta.arch != NULL) {
267
3
		md_word("(");
268
3
		md_word(mdoc->meta.arch);
269
3
		md_word(")");
270
3
	}
271
654
	outflags |= MD_sp;
272
273
654
	md_nodelist(mdoc->first->child);
274
275
654
	outflags |= MD_sp;
276
654
	md_word(mdoc->meta.os);
277
654
	md_word("-");
278
654
	md_word(mdoc->meta.date);
279
1308
	putchar('\n');
280
654
}
281
282
static void
283
md_nodelist(struct roff_node *n)
284
{
285
119676
	while (n != NULL) {
286
35385
		md_node(n);
287
35385
		n = n->next;
288
	}
289
16302
}
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
71868
	if (n->flags & NODE_NOPRT)
298
1986
		return;
299
300
33948
	if (outflags & MD_nonl)
301
708
		outflags &= ~(MD_nl | MD_sp);
302

60867
	else if (outflags & MD_spc && n->flags & NODE_LINE)
303
11880
		outflags |= MD_nl;
304
305
	act = NULL;
306
	cond = 0;
307
	process_children = 1;
308
33948
	n->flags &= ~NODE_ENDED;
309
310
33948
	if (n->type == ROFFT_TEXT) {
311
13863
		if (n->flags & NODE_DELIMC)
312
756
			outflags &= ~(MD_spc | MD_spc_force);
313
13107
		else if (outflags & MD_Sm)
314
12948
			outflags |= MD_spc_force;
315
27567
		md_word(n->string);
316
13863
		if (n->flags & NODE_DELIMO)
317
264
			outflags &= ~(MD_spc | MD_spc_force);
318
13599
		else if (outflags & MD_Sm)
319
13437
			outflags |= MD_spc;
320
20085
	} else if (n->tok < ROFF_MAX) {
321
42
		switch (n->tok) {
322
		case ROFF_br:
323
39
			process_children = md_pre_br(n);
324
39
			break;
325
		case ROFF_sp:
326
3
			process_children = md_pre_Pp(n);
327
3
			break;
328
		default:
329
			process_children = 0;
330
			break;
331
		}
332
	} else {
333

40086
		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
334
20043
		act = md_acts + n->tok;
335
46851
		cond = act->cond == NULL || (*act->cond)(n);
336

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

66090
	if (process_children && n->child != NULL)
342
15648
		md_nodelist(n->child);
343
344
33948
	if (n->flags & NODE_ENDED)
345
129
		return;
346
347

49293
	if (cond && act->post != NULL)
348
8889
		(*act->post)(n);
349
350
33819
	if (n->end != ENDBODY_NOT)
351
129
		n->body->flags |= NODE_ENDED;
352
69753
}
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
25644
	switch (c) {
362
	case '\0':
363
		break;
364
	case (char)-1:
365
906
		assert(cur);
366
906
		stack[--cur] = '\0';
367
906
		break;
368
	default:
369
906
		if (cur + 1 >= sz) {
370
132
			sz += 8;
371
132
			stack = mandoc_realloc(stack, sz);
372
132
		}
373
906
		stack[cur] = c;
374
906
		stack[++cur] = '\0';
375
906
		break;
376
	}
377
13728
	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

63300
	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
31650
	if (outflags & MD_sp)
407
11316
		putchar('\n');
408
25992
	else if (outflags & MD_br) {
409
672
		putchar(' ');
410
672
		putchar(' ');
411

29766
	} else if (outflags & MD_nl && escflags & ESC_EOL)
412
6
		md_named("zwnj");
413
414
	/* Start a new line if necessary. */
415
416
31650
	if (outflags & (MD_nl | MD_br | MD_sp)) {
417
20208
		putchar('\n');
418
22044
		for (cp = md_stack('\0'); *cp != '\0'; cp++) {
419
1836
			putchar(*cp);
420
918
			if (*cp == '>')
421
1254
				putchar(' ');
422
		}
423
10104
		outflags &= ~(MD_nl | MD_br | MD_sp);
424
10104
		escflags = ESC_BOL;
425
		outcount = 0;
426
427
	/* Handle horizontal spacing. */
428
429
31650
	} else if (outflags & MD_spc) {
430
7482
		if (outflags & MD_Bk)
431
42
			fputs("&nbsp;", stdout);
432
		else
433
14880
			putchar(' ');
434
7482
		escflags &= ~ESC_FON;
435
7482
		outcount++;
436
7482
	}
437
438
49236
	outflags &= ~(MD_spc_force | MD_nonl);
439
63300
	if (outflags & MD_Sm)
440
31650
		outflags |= MD_spc;
441
	else
442
31650
		outflags &= ~MD_spc;
443
31650
}
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
18234
	md_preword();
454
455
9117
	if (*s == '\0')
456
		return;
457
458
9117
	if (escflags & ESC_FON) {
459
51
		escflags &= ~ESC_FON;
460
51
		if (*s == '*' && !code_blocks)
461
51
			fputs("&zwnj;", stdout);
462
	}
463
464
45816
	while (*s != '\0') {
465

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

47643
	if (s[0] != '\0' && s[1] == '\0' &&
500
9567
	    strchr("!),.:;?]", s[0]) != NULL &&
501
2799
	    (outflags & MD_spc_force) == 0)
502
2322
		outflags &= ~MD_spc;
503
504
22434
	md_preword();
505
506
22434
	if (*s == '\0')
507
24
		return;
508
509
	/* No spacing after opening delimiters. */
510

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




195333
		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
114
			bs = escflags & ESC_BOL && !code_blocks;
528
			c = '-';
529
57
			break;
530
		case ASCII_BREAK:
531
			continue;
532
		case '#':
533
		case '+':
534
		case '-':
535
6990
			bs = escflags & ESC_BOL && !code_blocks;
536
3492
			break;
537
		case '(':
538
2427
			bs = escflags & ESC_HYP && !code_blocks;
539
1209
			break;
540
		case ')':
541
2634
			bs = escflags & ESC_NUM && !code_blocks;
542
1317
			break;
543
		case '*':
544
		case '[':
545
		case '_':
546
		case '`':
547
813
			bs = !code_blocks;
548
813
			break;
549
		case '.':
550
2013
			bs = escflags & ESC_NUM && !code_blocks;
551
996
			break;
552
		case '<':
553
243
			if (code_blocks == 0) {
554
240
				md_named("lt");
555
				c = '\0';
556
240
			}
557
			break;
558
		case '=':
559
12
			if (escflags & ESC_BOL && !code_blocks) {
560
				md_named("equals");
561
				c = '\0';
562
			}
563
			break;
564
		case '>':
565
240
			if (code_blocks == 0) {
566
237
				md_named("gt");
567
				c = '\0';
568
237
			}
569
			break;
570
		case '\\':
571
			uc = 0;
572
			nextfont = NULL;
573


690
			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
195
				uc = mchars_spec2cp(seq, sz);
582
195
				break;
583
			case ESCAPE_FONTBOLD:
584
				nextfont = "**";
585
30
				break;
586
			case ESCAPE_FONTITALIC:
587
				nextfont = "*";
588
45
				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
75
				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
345
			if (nextfont != NULL && !code_blocks) {
612
120
				if (*currfont != '\0') {
613
60
					outflags &= ~MD_spc;
614
60
					md_rawword(currfont);
615
60
				}
616
				prevfont = currfont;
617
				currfont = nextfont;
618
120
				if (*currfont != '\0') {
619
60
					outflags &= ~MD_spc;
620
60
					md_rawword(currfont);
621
60
				}
622
			}
623
345
			if (uc) {
624
159
				if ((uc < 0x20 && uc != 0x09) ||
625
159
				    (uc > 0x7E && uc < 0xA0))
626
					uc = 0xFFFD;
627
159
				if (code_blocks) {
628
					seq = mchars_uc2str(uc);
629
					fputs(seq, stdout);
630
					outcount += strlen(seq);
631
				} else {
632
159
					printf("&#%d;", uc);
633
159
					outcount++;
634
				}
635
159
				escflags &= ~ESC_FON;
636
159
			}
637
			c = '\0';
638
345
			break;
639
		case ']':
640
876
			bs = escflags & ESC_SQU && !code_blocks;
641
438
			escflags |= ESC_HYP;
642
438
			break;
643
		default:
644
			break;
645
		}
646
186516
		if (bs)
647
1668
			putchar('\\');
648
186516
		md_char(c);
649

186516
		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
22410
	if (*currfont != '\0') {
658
		outflags &= ~MD_spc;
659
		md_rawword(currfont);
660
44820
	} else if (s[-2] == ' ')
661
22410
		escflags |= ESC_EOL;
662
	else
663
22410
		escflags &= ~ESC_EOL;
664
44844
}
665
666
/*
667
 * Print a single HTML named character reference.
668
 */
669
static void
670
md_named(const char *s)
671
{
672
966
	printf("&%s;", s);
673
483
	escflags &= ~(ESC_FON | ESC_EOL);
674
483
	outcount++;
675
483
}
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
400614
	if (c != '\0') {
684
398970
		putchar(c);
685
398970
		if (c == '*')
686
199485
			escflags |= ESC_FON;
687
		else
688
199485
			escflags &= ~ESC_FON;
689
199485
		outcount++;
690
199485
	}
691
200307
	if (c != ']')
692
199812
		escflags &= ~ESC_HYP;
693

544683
	if (c == ' ' || c == '\t' || c == '>')
694
		return;
695
172026
	if (isdigit(c) == 0)
696
166758
		escflags &= ~ESC_NUM;
697
5268
	else if (escflags & ESC_BOL)
698
51
		escflags |= ESC_NUM;
699
338835
	escflags &= ~ESC_BOL;
700
372333
}
701
702
static int
703
md_cond_head(struct roff_node *n)
704
{
705
3870
	return n->type == ROFFT_HEAD;
706
}
707
708
static int
709
md_cond_body(struct roff_node *n)
710
{
711
9660
	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
6072
	if ((prefix = md_acts[n->tok].prefix) != NULL) {
720
3036
		md_rawword(prefix);
721
3036
		outflags &= ~MD_spc;
722
3036
		if (*prefix == '`')
723
300
			code_blocks++;
724
	}
725
3036
	return 1;
726
}
727
728
static void
729
md_post_raw(struct roff_node *n)
730
{
731
	const char	*suffix;
732
733
6276
	if ((suffix = md_acts[n->tok].suffix) != NULL) {
734
3036
		outflags &= ~(MD_spc | MD_nl);
735
3036
		md_rawword(suffix);
736
3036
		if (*suffix == '`')
737
303
			code_blocks--;
738
	}
739
3138
}
740
741
static int
742
md_pre_word(struct roff_node *n)
743
{
744
	const char	*prefix;
745
746
1080
	if ((prefix = md_acts[n->tok].prefix) != NULL) {
747
540
		md_word(prefix);
748
540
		outflags &= ~MD_spc;
749
540
	}
750
540
	return 1;
751
}
752
753
static void
754
md_post_word(struct roff_node *n)
755
{
756
	const char	*suffix;
757
758
1080
	if ((suffix = md_acts[n->tok].suffix) != NULL) {
759
540
		outflags &= ~(MD_spc | MD_nl);
760
540
		md_word(suffix);
761
540
	}
762
540
}
763
764
static void
765
md_post_pc(struct roff_node *n)
766
{
767
270
	md_post_raw(n);
768
135
	if (n->parent->tok != MDOC_Rs)
769
		return;
770
135
	if (n->next != NULL) {
771
111
		md_word(",");
772

117
		if (n->prev != NULL &&
773
93
		    n->prev->tok == n->tok &&
774
6
		    n->next->tok == n->tok)
775
3
			md_word("and");
776
	} else {
777
24
		md_word(".");
778
24
		outflags |= MD_nl;
779
	}
780
135
}
781
782
static int
783
md_pre_skip(struct roff_node *n)
784
{
785
12
	return 0;
786
}
787
788
static void
789
md_pre_syn(struct roff_node *n)
790
{
791

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

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

201
	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
63
		outflags |= MD_sp;
809
63
		break;
810
	case MDOC_Ft:
811

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

69
		else if (n->sec == SEC_AUTHORS &&
838
21
		    ! (outflags & MD_An_nosplit))
839
6
			outflags |= MD_An_split;
840
78
		return 1;
841
	}
842
87
}
843
844
static int
845
md_pre_Ap(struct roff_node *n)
846
{
847
24
	outflags &= ~MD_spc;
848
12
	md_word("'");
849
12
	outflags &= ~MD_spc;
850
12
	return 0;
851
}
852
853
static int
854
md_pre_Bd(struct roff_node *n)
855
{
856
282
	switch (n->norm->Bd.type) {
857
	case DISP_unfilled:
858
	case DISP_literal:
859
48
		return md_pre_Dl(n);
860
	default:
861
93
		return md_pre_D1(n);
862
	}
863
141
}
864
865
static int
866
md_pre_Bk(struct roff_node *n)
867
{
868
36
	switch (n->type) {
869
	case ROFFT_BLOCK:
870
6
		return 1;
871
	case ROFFT_BODY:
872
6
		outflags |= MD_Bk;
873
6
		return 1;
874
	default:
875
6
		return 0;
876
	}
877
18
}
878
879
static void
880
md_post_Bk(struct roff_node *n)
881
{
882
36
	if (n->type == ROFFT_BODY)
883
6
		outflags &= ~MD_Bk;
884
18
}
885
886
static int
887
md_pre_Bl(struct roff_node *n)
888
{
889
1488
	n->norm->Bl.count = 0;
890
744
	if (n->norm->Bl.type == LIST_column)
891
63
		md_pre_Dl(n);
892
744
	outflags |= MD_sp;
893
744
	return 1;
894
}
895
896
static void
897
md_post_Bl(struct roff_node *n)
898
{
899
1488
	n->norm->Bl.count = 0;
900
744
	if (n->norm->Bl.type == LIST_column)
901
63
		md_post_D1(n);
902
744
	outflags |= MD_sp;
903
744
}
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
882
	if (code_blocks) {
913
		md_stack('\t');
914
		code_blocks++;
915
	} else {
916
441
		md_stack('>');
917
		quote_blocks++;
918
	}
919
441
	outflags |= MD_sp;
920
441
	return 1;
921
}
922
923
static void
924
md_post_D1(struct roff_node *n)
925
{
926
1128
	md_stack((char)-1);
927
564
	if (code_blocks)
928
		code_blocks--;
929
	else
930
		quote_blocks--;
931
564
	outflags |= MD_sp;
932
564
}
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
246
	if (quote_blocks) {
942
3
		md_stack('>');
943
		quote_blocks++;
944
3
	} else {
945
120
		md_stack('\t');
946
		code_blocks++;
947
	}
948
123
	outflags |= MD_sp;
949
123
	return 1;
950
}
951
952
static int
953
md_pre_En(struct roff_node *n)
954
{
955

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

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

153
	if (n->end == ENDBODY_NOT &&
980
63
	    n->parent->head->child == NULL &&
981
27
	    n->child != NULL &&
982
15
	    n->child->end != ENDBODY_NOT)
983
3
		md_preword();
984

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

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

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

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

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

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

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

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

2016
		if (bln->norm->Bl.comp == 0 &&
1172
900
		    bln->norm->Bl.type != LIST_column)
1173
741
			outflags |= MD_sp;
1174
1116
		outflags |= MD_nl;
1175
1176



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

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


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

1683
		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
1269
240
			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
321
		nc = bln->norm->Bl.ncols;
1278
1266
		i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
1279
660
		    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
1280
321
		if (i < 1)
1281
			i = 1;
1282
4584
		while (i-- > 0)
1283
3942
			putchar(' ');
1284
1285
321
		outflags &= ~MD_spc;
1286
321
		escflags &= ~ESC_FON;
1287
321
		outcount = 0;
1288
321
		break;
1289
1290
	default:
1291
		break;
1292
	}
1293
5106
}
1294
1295
static void
1296
md_post_Lb(struct roff_node *n)
1297
{
1298
30
	if (n->sec == SEC_LIBRARY)
1299
9
		outflags |= MD_br;
1300
15
}
1301
1302
static void
1303
md_uri(const char *s)
1304
{
1305
2211
	while (*s != '\0') {
1306
1020
		if (strchr("%()<>", *s) != NULL) {
1307
			printf("%%%2.2hhX", *s);
1308
			outcount += 3;
1309
		} else {
1310
2040
			putchar(*s);
1311
			outcount++;
1312
		}
1313
1020
		s++;
1314
	}
1315
57
}
1316
1317
static int
1318
md_pre_Lk(struct roff_node *n)
1319
{
1320
	const struct roff_node *link, *descr, *punct;
1321
1322
72
	if ((link = n->child) == NULL)
1323
		return 0;
1324
1325
	/* Find beginning of trailing punctuation. */
1326
36
	punct = n->last;
1327

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

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

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

93
	if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1441
21
		outflags &= ~MD_spc;
1442
33
}
1443
1444
static int
1445
md_pre_Pp(struct roff_node *n)
1446
{
1447
1062
	outflags |= MD_sp;
1448
531
	return 0;
1449
}
1450
1451
static int
1452
md_pre_Rs(struct roff_node *n)
1453
{
1454
60
	if (n->sec == SEC_SEE_ALSO)
1455
18
		outflags |= MD_sp;
1456
30
	return 1;
1457
}
1458
1459
static int
1460
md_pre_Sh(struct roff_node *n)
1461
{
1462

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

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

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

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