GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_man.c Lines: 0 646 0.0 %
Date: 2016-12-06 Branches: 0 531 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mdoc_man.c,v 1.94 2016/01/08 17:48:04 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2011-2016 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 AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <stdio.h>
21
#include <string.h>
22
23
#include "mandoc_aux.h"
24
#include "mandoc.h"
25
#include "roff.h"
26
#include "mdoc.h"
27
#include "man.h"
28
#include "out.h"
29
#include "main.h"
30
31
#define	DECL_ARGS const struct roff_meta *meta, struct roff_node *n
32
33
struct	manact {
34
	int		(*cond)(DECL_ARGS); /* DON'T run actions */
35
	int		(*pre)(DECL_ARGS); /* pre-node action */
36
	void		(*post)(DECL_ARGS); /* post-node action */
37
	const char	 *prefix; /* pre-node string constant */
38
	const char	 *suffix; /* post-node string constant */
39
};
40
41
static	int	  cond_body(DECL_ARGS);
42
static	int	  cond_head(DECL_ARGS);
43
static  void	  font_push(char);
44
static	void	  font_pop(void);
45
static	void	  mid_it(void);
46
static	void	  post__t(DECL_ARGS);
47
static	void	  post_aq(DECL_ARGS);
48
static	void	  post_bd(DECL_ARGS);
49
static	void	  post_bf(DECL_ARGS);
50
static	void	  post_bk(DECL_ARGS);
51
static	void	  post_bl(DECL_ARGS);
52
static	void	  post_dl(DECL_ARGS);
53
static	void	  post_en(DECL_ARGS);
54
static	void	  post_enc(DECL_ARGS);
55
static	void	  post_eo(DECL_ARGS);
56
static	void	  post_fa(DECL_ARGS);
57
static	void	  post_fd(DECL_ARGS);
58
static	void	  post_fl(DECL_ARGS);
59
static	void	  post_fn(DECL_ARGS);
60
static	void	  post_fo(DECL_ARGS);
61
static	void	  post_font(DECL_ARGS);
62
static	void	  post_in(DECL_ARGS);
63
static	void	  post_it(DECL_ARGS);
64
static	void	  post_lb(DECL_ARGS);
65
static	void	  post_nm(DECL_ARGS);
66
static	void	  post_percent(DECL_ARGS);
67
static	void	  post_pf(DECL_ARGS);
68
static	void	  post_sect(DECL_ARGS);
69
static	void	  post_sp(DECL_ARGS);
70
static	void	  post_vt(DECL_ARGS);
71
static	int	  pre__t(DECL_ARGS);
72
static	int	  pre_an(DECL_ARGS);
73
static	int	  pre_ap(DECL_ARGS);
74
static	int	  pre_aq(DECL_ARGS);
75
static	int	  pre_bd(DECL_ARGS);
76
static	int	  pre_bf(DECL_ARGS);
77
static	int	  pre_bk(DECL_ARGS);
78
static	int	  pre_bl(DECL_ARGS);
79
static	int	  pre_br(DECL_ARGS);
80
static	int	  pre_bx(DECL_ARGS);
81
static	int	  pre_dl(DECL_ARGS);
82
static	int	  pre_en(DECL_ARGS);
83
static	int	  pre_enc(DECL_ARGS);
84
static	int	  pre_em(DECL_ARGS);
85
static	int	  pre_skip(DECL_ARGS);
86
static	int	  pre_eo(DECL_ARGS);
87
static	int	  pre_ex(DECL_ARGS);
88
static	int	  pre_fa(DECL_ARGS);
89
static	int	  pre_fd(DECL_ARGS);
90
static	int	  pre_fl(DECL_ARGS);
91
static	int	  pre_fn(DECL_ARGS);
92
static	int	  pre_fo(DECL_ARGS);
93
static	int	  pre_ft(DECL_ARGS);
94
static	int	  pre_in(DECL_ARGS);
95
static	int	  pre_it(DECL_ARGS);
96
static	int	  pre_lk(DECL_ARGS);
97
static	int	  pre_li(DECL_ARGS);
98
static	int	  pre_ll(DECL_ARGS);
99
static	int	  pre_nm(DECL_ARGS);
100
static	int	  pre_no(DECL_ARGS);
101
static	int	  pre_ns(DECL_ARGS);
102
static	int	  pre_pp(DECL_ARGS);
103
static	int	  pre_rs(DECL_ARGS);
104
static	int	  pre_rv(DECL_ARGS);
105
static	int	  pre_sm(DECL_ARGS);
106
static	int	  pre_sp(DECL_ARGS);
107
static	int	  pre_sect(DECL_ARGS);
108
static	int	  pre_sy(DECL_ARGS);
109
static	void	  pre_syn(const struct roff_node *);
110
static	int	  pre_vt(DECL_ARGS);
111
static	int	  pre_ux(DECL_ARGS);
112
static	int	  pre_xr(DECL_ARGS);
113
static	void	  print_word(const char *);
114
static	void	  print_line(const char *, int);
115
static	void	  print_block(const char *, int);
116
static	void	  print_offs(const char *, int);
117
static	void	  print_width(const struct mdoc_bl *,
118
			const struct roff_node *);
119
static	void	  print_count(int *);
120
static	void	  print_node(DECL_ARGS);
121
122
static	const struct manact manacts[MDOC_MAX + 1] = {
123
	{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
124
	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
125
	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
126
	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
127
	{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
128
	{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
129
	{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
130
	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
131
	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
132
	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
133
	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
134
	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
135
	{ NULL, NULL, NULL, NULL, NULL }, /* El */
136
	{ NULL, pre_it, post_it, NULL, NULL }, /* It */
137
	{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
138
	{ NULL, pre_an, NULL, NULL, NULL }, /* An */
139
	{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
140
	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
141
	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
142
	{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
143
	{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
144
	{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
145
	{ NULL, pre_ex, NULL, NULL, NULL }, /* Ex */
146
	{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
147
	{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
148
	{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
149
	{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
150
	{ NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
151
	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
152
	{ NULL, pre_in, post_in, NULL, NULL }, /* In */
153
	{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
154
	{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
155
	{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
156
	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
157
	{ NULL, pre_ft, post_font, NULL, NULL }, /* Ot */
158
	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
159
	{ NULL, pre_rv, NULL, NULL, NULL }, /* Rv */
160
	{ NULL, NULL, NULL, NULL, NULL }, /* St */
161
	{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
162
	{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
163
	{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
164
	{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
165
	{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
166
	{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
167
	{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
168
	{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
169
	{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
170
	{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
171
	{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
172
	{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
173
	{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
174
	{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
175
	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
176
	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Ao */
177
	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Aq */
178
	{ NULL, NULL, NULL, NULL, NULL }, /* At */
179
	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
180
	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
181
	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
182
	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
183
	{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
184
	{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
185
	{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
186
	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
187
	{ cond_body, pre_enc, post_enc, "\\(Lq", "\\(Rq" }, /* Do */
188
	{ cond_body, pre_enc, post_enc, "\\(Lq", "\\(Rq" }, /* Dq */
189
	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
190
	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
191
	{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
192
	{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
193
	{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
194
	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
195
	{ NULL, pre_no, NULL, NULL, NULL }, /* No */
196
	{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
197
	{ NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
198
	{ NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
199
	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
200
	{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
201
	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
202
	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
203
	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
204
	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
205
	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
206
	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
207
	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
208
	{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
209
	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
210
	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
211
	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
212
	{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
213
	{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
214
	{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
215
	{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
216
	{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
217
	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
218
	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
219
	{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
220
	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
221
	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
222
	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
223
	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
224
	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
225
	{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
226
	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
227
	{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
228
	{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
229
	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
230
	{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
231
	{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
232
	{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
233
	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
234
	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
235
	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
236
	{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
237
	{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
238
	{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
239
	{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
240
	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
241
	{ NULL, pre_br, NULL, NULL, NULL }, /* br */
242
	{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
243
	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
244
	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
245
	{ NULL, pre_ll, post_sp, NULL, NULL }, /* ll */
246
	{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
247
};
248
249
static	int		outflags;
250
#define	MMAN_spc	(1 << 0)  /* blank character before next word */
251
#define	MMAN_spc_force	(1 << 1)  /* even before trailing punctuation */
252
#define	MMAN_nl		(1 << 2)  /* break man(7) code line */
253
#define	MMAN_br		(1 << 3)  /* break output line */
254
#define	MMAN_sp		(1 << 4)  /* insert a blank output line */
255
#define	MMAN_PP		(1 << 5)  /* reset indentation etc. */
256
#define	MMAN_Sm		(1 << 6)  /* horizontal spacing mode */
257
#define	MMAN_Bk		(1 << 7)  /* word keep mode */
258
#define	MMAN_Bk_susp	(1 << 8)  /* suspend this (after a macro) */
259
#define	MMAN_An_split	(1 << 9)  /* author mode is "split" */
260
#define	MMAN_An_nosplit	(1 << 10) /* author mode is "nosplit" */
261
#define	MMAN_PD		(1 << 11) /* inter-paragraph spacing disabled */
262
#define	MMAN_nbrword	(1 << 12) /* do not break the next word */
263
264
#define	BL_STACK_MAX	32
265
266
static	int		Bl_stack[BL_STACK_MAX];  /* offsets [chars] */
267
static	int		Bl_stack_post[BL_STACK_MAX];  /* add final .RE */
268
static	int		Bl_stack_len;  /* number of nested Bl blocks */
269
static	int		TPremain;  /* characters before tag is full */
270
271
static	struct {
272
	char	*head;
273
	char	*tail;
274
	size_t	 size;
275
}	fontqueue;
276
277
278
static void
279
font_push(char newfont)
280
{
281
282
	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
283
		fontqueue.size += 8;
284
		fontqueue.head = mandoc_realloc(fontqueue.head,
285
		    fontqueue.size);
286
	}
287
	*fontqueue.tail = newfont;
288
	print_word("");
289
	printf("\\f");
290
	putchar(newfont);
291
	outflags &= ~MMAN_spc;
292
}
293
294
static void
295
font_pop(void)
296
{
297
298
	if (fontqueue.tail > fontqueue.head)
299
		fontqueue.tail--;
300
	outflags &= ~MMAN_spc;
301
	print_word("");
302
	printf("\\f");
303
	putchar(*fontqueue.tail);
304
}
305
306
static void
307
print_word(const char *s)
308
{
309
310
	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
311
		/*
312
		 * If we need a newline, print it now and start afresh.
313
		 */
314
		if (MMAN_PP & outflags) {
315
			if (MMAN_sp & outflags) {
316
				if (MMAN_PD & outflags) {
317
					printf("\n.PD");
318
					outflags &= ~MMAN_PD;
319
				}
320
			} else if ( ! (MMAN_PD & outflags)) {
321
				printf("\n.PD 0");
322
				outflags |= MMAN_PD;
323
			}
324
			printf("\n.PP\n");
325
		} else if (MMAN_sp & outflags)
326
			printf("\n.sp\n");
327
		else if (MMAN_br & outflags)
328
			printf("\n.br\n");
329
		else if (MMAN_nl & outflags)
330
			putchar('\n');
331
		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
332
		if (1 == TPremain)
333
			printf(".br\n");
334
		TPremain = 0;
335
	} else if (MMAN_spc & outflags) {
336
		/*
337
		 * If we need a space, only print it if
338
		 * (1) it is forced by `No' or
339
		 * (2) what follows is not terminating punctuation or
340
		 * (3) what follows is longer than one character.
341
		 */
342
		if (MMAN_spc_force & outflags || '\0' == s[0] ||
343
		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
344
			if (MMAN_Bk & outflags &&
345
			    ! (MMAN_Bk_susp & outflags))
346
				putchar('\\');
347
			putchar(' ');
348
			if (TPremain)
349
				TPremain--;
350
		}
351
	}
352
353
	/*
354
	 * Reassign needing space if we're not following opening
355
	 * punctuation.
356
	 */
357
	if (MMAN_Sm & outflags && ('\0' == s[0] ||
358
	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
359
		outflags |= MMAN_spc;
360
	else
361
		outflags &= ~MMAN_spc;
362
	outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
363
364
	for ( ; *s; s++) {
365
		switch (*s) {
366
		case ASCII_NBRSP:
367
			printf("\\ ");
368
			break;
369
		case ASCII_HYPH:
370
			putchar('-');
371
			break;
372
		case ASCII_BREAK:
373
			printf("\\:");
374
			break;
375
		case ' ':
376
			if (MMAN_nbrword & outflags) {
377
				printf("\\ ");
378
				break;
379
			}
380
			/* FALLTHROUGH */
381
		default:
382
			putchar((unsigned char)*s);
383
			break;
384
		}
385
		if (TPremain)
386
			TPremain--;
387
	}
388
	outflags &= ~MMAN_nbrword;
389
}
390
391
static void
392
print_line(const char *s, int newflags)
393
{
394
395
	outflags &= ~MMAN_br;
396
	outflags |= MMAN_nl;
397
	print_word(s);
398
	outflags |= newflags;
399
}
400
401
static void
402
print_block(const char *s, int newflags)
403
{
404
405
	outflags &= ~MMAN_PP;
406
	if (MMAN_sp & outflags) {
407
		outflags &= ~(MMAN_sp | MMAN_br);
408
		if (MMAN_PD & outflags) {
409
			print_line(".PD", 0);
410
			outflags &= ~MMAN_PD;
411
		}
412
	} else if (! (MMAN_PD & outflags))
413
		print_line(".PD 0", MMAN_PD);
414
	outflags |= MMAN_nl;
415
	print_word(s);
416
	outflags |= MMAN_Bk_susp | newflags;
417
}
418
419
static void
420
print_offs(const char *v, int keywords)
421
{
422
	char		  buf[24];
423
	struct roffsu	  su;
424
	int		  sz;
425
426
	print_line(".RS", MMAN_Bk_susp);
427
428
	/* Convert v into a number (of characters). */
429
	if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
430
		sz = 0;
431
	else if (keywords && !strcmp(v, "indent"))
432
		sz = 6;
433
	else if (keywords && !strcmp(v, "indent-two"))
434
		sz = 12;
435
	else if (a2roffsu(v, &su, SCALE_EN) > 1) {
436
		if (SCALE_EN == su.unit)
437
			sz = su.scale;
438
		else {
439
			/*
440
			 * XXX
441
			 * If we are inside an enclosing list,
442
			 * there is no easy way to add the two
443
			 * indentations because they are provided
444
			 * in terms of different units.
445
			 */
446
			print_word(v);
447
			outflags |= MMAN_nl;
448
			return;
449
		}
450
	} else
451
		sz = strlen(v);
452
453
	/*
454
	 * We are inside an enclosing list.
455
	 * Add the two indentations.
456
	 */
457
	if (Bl_stack_len)
458
		sz += Bl_stack[Bl_stack_len - 1];
459
460
	(void)snprintf(buf, sizeof(buf), "%dn", sz);
461
	print_word(buf);
462
	outflags |= MMAN_nl;
463
}
464
465
/*
466
 * Set up the indentation for a list item; used from pre_it().
467
 */
468
static void
469
print_width(const struct mdoc_bl *bl, const struct roff_node *child)
470
{
471
	char		  buf[24];
472
	struct roffsu	  su;
473
	int		  numeric, remain, sz, chsz;
474
475
	numeric = 1;
476
	remain = 0;
477
478
	/* Convert the width into a number (of characters). */
479
	if (bl->width == NULL)
480
		sz = (bl->type == LIST_hang) ? 6 : 0;
481
	else if (a2roffsu(bl->width, &su, SCALE_MAX) > 1) {
482
		if (SCALE_EN == su.unit)
483
			sz = su.scale;
484
		else {
485
			sz = 0;
486
			numeric = 0;
487
		}
488
	} else
489
		sz = strlen(bl->width);
490
491
	/* XXX Rough estimation, might have multiple parts. */
492
	if (bl->type == LIST_enum)
493
		chsz = (bl->count > 8) + 1;
494
	else if (child != NULL && child->type == ROFFT_TEXT)
495
		chsz = strlen(child->string);
496
	else
497
		chsz = 0;
498
499
	/* Maybe we are inside an enclosing list? */
500
	mid_it();
501
502
	/*
503
	 * Save our own indentation,
504
	 * such that child lists can use it.
505
	 */
506
	Bl_stack[Bl_stack_len++] = sz + 2;
507
508
	/* Set up the current list. */
509
	if (chsz > sz && bl->type != LIST_tag)
510
		print_block(".HP", 0);
511
	else {
512
		print_block(".TP", 0);
513
		remain = sz + 2;
514
	}
515
	if (numeric) {
516
		(void)snprintf(buf, sizeof(buf), "%dn", sz + 2);
517
		print_word(buf);
518
	} else
519
		print_word(bl->width);
520
	TPremain = remain;
521
}
522
523
static void
524
print_count(int *count)
525
{
526
	char		  buf[24];
527
528
	(void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
529
	print_word(buf);
530
}
531
532
void
533
man_man(void *arg, const struct roff_man *man)
534
{
535
536
	/*
537
	 * Dump the keep buffer.
538
	 * We're guaranteed by now that this exists (is non-NULL).
539
	 * Flush stdout afterward, just in case.
540
	 */
541
	fputs(mparse_getkeep(man_mparse(man)), stdout);
542
	fflush(stdout);
543
}
544
545
void
546
man_mdoc(void *arg, const struct roff_man *mdoc)
547
{
548
	struct roff_node *n;
549
550
	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
551
	    mdoc->meta.title,
552
	    (mdoc->meta.msec == NULL ? "" : mdoc->meta.msec),
553
	    mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol);
554
555
	/* Disable hyphenation and if nroff, disable justification. */
556
	printf(".nh\n.if n .ad l");
557
558
	outflags = MMAN_nl | MMAN_Sm;
559
	if (0 == fontqueue.size) {
560
		fontqueue.size = 8;
561
		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
562
		*fontqueue.tail = 'R';
563
	}
564
	for (n = mdoc->first->child; n != NULL; n = n->next)
565
		print_node(&mdoc->meta, n);
566
	putchar('\n');
567
}
568
569
static void
570
print_node(DECL_ARGS)
571
{
572
	const struct manact	*act;
573
	struct roff_node	*sub;
574
	int			 cond, do_sub;
575
576
	/*
577
	 * Break the line if we were parsed subsequent the current node.
578
	 * This makes the page structure be more consistent.
579
	 */
580
	if (MMAN_spc & outflags && MDOC_LINE & n->flags)
581
		outflags |= MMAN_nl;
582
583
	act = NULL;
584
	cond = 0;
585
	do_sub = 1;
586
	n->flags &= ~MDOC_ENDED;
587
588
	if (n->type == ROFFT_TEXT) {
589
		/*
590
		 * Make sure that we don't happen to start with a
591
		 * control character at the start of a line.
592
		 */
593
		if (MMAN_nl & outflags &&
594
		    ('.' == *n->string || '\'' == *n->string)) {
595
			print_word("");
596
			printf("\\&");
597
			outflags &= ~MMAN_spc;
598
		}
599
		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMC))
600
			outflags |= MMAN_spc_force;
601
		print_word(n->string);
602
		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMO))
603
			outflags |= MMAN_spc;
604
	} else {
605
		/*
606
		 * Conditionally run the pre-node action handler for a
607
		 * node.
608
		 */
609
		act = manacts + n->tok;
610
		cond = act->cond == NULL || (*act->cond)(meta, n);
611
		if (cond && act->pre != NULL &&
612
		    (n->end == ENDBODY_NOT || n->child != NULL))
613
			do_sub = (*act->pre)(meta, n);
614
	}
615
616
	/*
617
	 * Conditionally run all child nodes.
618
	 * Note that this iterates over children instead of using
619
	 * recursion.  This prevents unnecessary depth in the stack.
620
	 */
621
	if (do_sub)
622
		for (sub = n->child; sub; sub = sub->next)
623
			print_node(meta, sub);
624
625
	/*
626
	 * Lastly, conditionally run the post-node handler.
627
	 */
628
	if (MDOC_ENDED & n->flags)
629
		return;
630
631
	if (cond && act->post)
632
		(*act->post)(meta, n);
633
634
	if (ENDBODY_NOT != n->end)
635
		n->body->flags |= MDOC_ENDED;
636
637
	if (ENDBODY_NOSPACE == n->end)
638
		outflags &= ~(MMAN_spc | MMAN_nl);
639
}
640
641
static int
642
cond_head(DECL_ARGS)
643
{
644
645
	return n->type == ROFFT_HEAD;
646
}
647
648
static int
649
cond_body(DECL_ARGS)
650
{
651
652
	return n->type == ROFFT_BODY;
653
}
654
655
static int
656
pre_enc(DECL_ARGS)
657
{
658
	const char	*prefix;
659
660
	prefix = manacts[n->tok].prefix;
661
	if (NULL == prefix)
662
		return 1;
663
	print_word(prefix);
664
	outflags &= ~MMAN_spc;
665
	return 1;
666
}
667
668
static void
669
post_enc(DECL_ARGS)
670
{
671
	const char *suffix;
672
673
	suffix = manacts[n->tok].suffix;
674
	if (NULL == suffix)
675
		return;
676
	outflags &= ~(MMAN_spc | MMAN_nl);
677
	print_word(suffix);
678
}
679
680
static int
681
pre_ex(DECL_ARGS)
682
{
683
	struct roff_node *nch;
684
685
	outflags |= MMAN_br | MMAN_nl;
686
687
	print_word("The");
688
689
	for (nch = n->child; nch != NULL; nch = nch->next) {
690
		font_push('B');
691
		print_word(nch->string);
692
		font_pop();
693
694
		if (nch->next == NULL)
695
			continue;
696
697
		if (nch->prev != NULL || nch->next->next != NULL) {
698
			outflags &= ~MMAN_spc;
699
			print_word(",");
700
		}
701
		if (nch->next->next == NULL)
702
			print_word("and");
703
	}
704
705
	if (n->child != NULL && n->child->next != NULL)
706
		print_word("utilities exit\\~0");
707
	else
708
		print_word("utility exits\\~0");
709
710
	print_word("on success, and\\~>0 if an error occurs.");
711
	outflags |= MMAN_nl;
712
	return 0;
713
}
714
715
static void
716
post_font(DECL_ARGS)
717
{
718
719
	font_pop();
720
}
721
722
static void
723
post_percent(DECL_ARGS)
724
{
725
726
	if (pre_em == manacts[n->tok].pre)
727
		font_pop();
728
	if (n->next) {
729
		print_word(",");
730
		if (n->prev &&	n->prev->tok == n->tok &&
731
				n->next->tok == n->tok)
732
			print_word("and");
733
	} else {
734
		print_word(".");
735
		outflags |= MMAN_nl;
736
	}
737
}
738
739
static int
740
pre__t(DECL_ARGS)
741
{
742
743
	if (n->parent && MDOC_Rs == n->parent->tok &&
744
	    n->parent->norm->Rs.quote_T) {
745
		print_word("");
746
		putchar('\"');
747
		outflags &= ~MMAN_spc;
748
	} else
749
		font_push('I');
750
	return 1;
751
}
752
753
static void
754
post__t(DECL_ARGS)
755
{
756
757
	if (n->parent && MDOC_Rs == n->parent->tok &&
758
	    n->parent->norm->Rs.quote_T) {
759
		outflags &= ~MMAN_spc;
760
		print_word("");
761
		putchar('\"');
762
	} else
763
		font_pop();
764
	post_percent(meta, n);
765
}
766
767
/*
768
 * Print before a section header.
769
 */
770
static int
771
pre_sect(DECL_ARGS)
772
{
773
774
	if (n->type == ROFFT_HEAD) {
775
		outflags |= MMAN_sp;
776
		print_block(manacts[n->tok].prefix, 0);
777
		print_word("");
778
		putchar('\"');
779
		outflags &= ~MMAN_spc;
780
	}
781
	return 1;
782
}
783
784
/*
785
 * Print subsequent a section header.
786
 */
787
static void
788
post_sect(DECL_ARGS)
789
{
790
791
	if (n->type != ROFFT_HEAD)
792
		return;
793
	outflags &= ~MMAN_spc;
794
	print_word("");
795
	putchar('\"');
796
	outflags |= MMAN_nl;
797
	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
798
		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
799
}
800
801
/* See mdoc_term.c, synopsis_pre() for comments. */
802
static void
803
pre_syn(const struct roff_node *n)
804
{
805
806
	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
807
		return;
808
809
	if (n->prev->tok == n->tok &&
810
	    MDOC_Ft != n->tok &&
811
	    MDOC_Fo != n->tok &&
812
	    MDOC_Fn != n->tok) {
813
		outflags |= MMAN_br;
814
		return;
815
	}
816
817
	switch (n->prev->tok) {
818
	case MDOC_Fd:
819
	case MDOC_Fn:
820
	case MDOC_Fo:
821
	case MDOC_In:
822
	case MDOC_Vt:
823
		outflags |= MMAN_sp;
824
		break;
825
	case MDOC_Ft:
826
		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
827
			outflags |= MMAN_sp;
828
			break;
829
		}
830
		/* FALLTHROUGH */
831
	default:
832
		outflags |= MMAN_br;
833
		break;
834
	}
835
}
836
837
static int
838
pre_an(DECL_ARGS)
839
{
840
841
	switch (n->norm->An.auth) {
842
	case AUTH_split:
843
		outflags &= ~MMAN_An_nosplit;
844
		outflags |= MMAN_An_split;
845
		return 0;
846
	case AUTH_nosplit:
847
		outflags &= ~MMAN_An_split;
848
		outflags |= MMAN_An_nosplit;
849
		return 0;
850
	default:
851
		if (MMAN_An_split & outflags)
852
			outflags |= MMAN_br;
853
		else if (SEC_AUTHORS == n->sec &&
854
		    ! (MMAN_An_nosplit & outflags))
855
			outflags |= MMAN_An_split;
856
		return 1;
857
	}
858
}
859
860
static int
861
pre_ap(DECL_ARGS)
862
{
863
864
	outflags &= ~MMAN_spc;
865
	print_word("'");
866
	outflags &= ~MMAN_spc;
867
	return 0;
868
}
869
870
static int
871
pre_aq(DECL_ARGS)
872
{
873
874
	print_word(n->child != NULL && n->child->next == NULL &&
875
	    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
876
	outflags &= ~MMAN_spc;
877
	return 1;
878
}
879
880
static void
881
post_aq(DECL_ARGS)
882
{
883
884
	outflags &= ~(MMAN_spc | MMAN_nl);
885
	print_word(n->child != NULL && n->child->next == NULL &&
886
	    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
887
}
888
889
static int
890
pre_bd(DECL_ARGS)
891
{
892
893
	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
894
895
	if (DISP_unfilled == n->norm->Bd.type ||
896
	    DISP_literal  == n->norm->Bd.type)
897
		print_line(".nf", 0);
898
	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
899
		outflags |= MMAN_sp;
900
	print_offs(n->norm->Bd.offs, 1);
901
	return 1;
902
}
903
904
static void
905
post_bd(DECL_ARGS)
906
{
907
908
	/* Close out this display. */
909
	print_line(".RE", MMAN_nl);
910
	if (DISP_unfilled == n->norm->Bd.type ||
911
	    DISP_literal  == n->norm->Bd.type)
912
		print_line(".fi", MMAN_nl);
913
914
	/* Maybe we are inside an enclosing list? */
915
	if (NULL != n->parent->next)
916
		mid_it();
917
}
918
919
static int
920
pre_bf(DECL_ARGS)
921
{
922
923
	switch (n->type) {
924
	case ROFFT_BLOCK:
925
		return 1;
926
	case ROFFT_BODY:
927
		break;
928
	default:
929
		return 0;
930
	}
931
	switch (n->norm->Bf.font) {
932
	case FONT_Em:
933
		font_push('I');
934
		break;
935
	case FONT_Sy:
936
		font_push('B');
937
		break;
938
	default:
939
		font_push('R');
940
		break;
941
	}
942
	return 1;
943
}
944
945
static void
946
post_bf(DECL_ARGS)
947
{
948
949
	if (n->type == ROFFT_BODY)
950
		font_pop();
951
}
952
953
static int
954
pre_bk(DECL_ARGS)
955
{
956
957
	switch (n->type) {
958
	case ROFFT_BLOCK:
959
		return 1;
960
	case ROFFT_BODY:
961
		outflags |= MMAN_Bk;
962
		return 1;
963
	default:
964
		return 0;
965
	}
966
}
967
968
static void
969
post_bk(DECL_ARGS)
970
{
971
972
	if (n->type == ROFFT_BODY)
973
		outflags &= ~MMAN_Bk;
974
}
975
976
static int
977
pre_bl(DECL_ARGS)
978
{
979
	size_t		 icol;
980
981
	/*
982
	 * print_offs() will increase the -offset to account for
983
	 * a possible enclosing .It, but any enclosed .It blocks
984
	 * just nest and do not add up their indentation.
985
	 */
986
	if (n->norm->Bl.offs) {
987
		print_offs(n->norm->Bl.offs, 0);
988
		Bl_stack[Bl_stack_len++] = 0;
989
	}
990
991
	switch (n->norm->Bl.type) {
992
	case LIST_enum:
993
		n->norm->Bl.count = 0;
994
		return 1;
995
	case LIST_column:
996
		break;
997
	default:
998
		return 1;
999
	}
1000
1001
	if (n->child != NULL) {
1002
		print_line(".TS", MMAN_nl);
1003
		for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1004
			print_word("l");
1005
		print_word(".");
1006
	}
1007
	outflags |= MMAN_nl;
1008
	return 1;
1009
}
1010
1011
static void
1012
post_bl(DECL_ARGS)
1013
{
1014
1015
	switch (n->norm->Bl.type) {
1016
	case LIST_column:
1017
		if (n->child != NULL)
1018
			print_line(".TE", 0);
1019
		break;
1020
	case LIST_enum:
1021
		n->norm->Bl.count = 0;
1022
		break;
1023
	default:
1024
		break;
1025
	}
1026
1027
	if (n->norm->Bl.offs) {
1028
		print_line(".RE", MMAN_nl);
1029
		assert(Bl_stack_len);
1030
		Bl_stack_len--;
1031
		assert(0 == Bl_stack[Bl_stack_len]);
1032
	} else {
1033
		outflags |= MMAN_PP | MMAN_nl;
1034
		outflags &= ~(MMAN_sp | MMAN_br);
1035
	}
1036
1037
	/* Maybe we are inside an enclosing list? */
1038
	if (NULL != n->parent->next)
1039
		mid_it();
1040
1041
}
1042
1043
static int
1044
pre_br(DECL_ARGS)
1045
{
1046
1047
	outflags |= MMAN_br;
1048
	return 0;
1049
}
1050
1051
static int
1052
pre_bx(DECL_ARGS)
1053
{
1054
1055
	n = n->child;
1056
	if (n) {
1057
		print_word(n->string);
1058
		outflags &= ~MMAN_spc;
1059
		n = n->next;
1060
	}
1061
	print_word("BSD");
1062
	if (NULL == n)
1063
		return 0;
1064
	outflags &= ~MMAN_spc;
1065
	print_word("-");
1066
	outflags &= ~MMAN_spc;
1067
	print_word(n->string);
1068
	return 0;
1069
}
1070
1071
static int
1072
pre_dl(DECL_ARGS)
1073
{
1074
1075
	print_offs("6n", 0);
1076
	return 1;
1077
}
1078
1079
static void
1080
post_dl(DECL_ARGS)
1081
{
1082
1083
	print_line(".RE", MMAN_nl);
1084
1085
	/* Maybe we are inside an enclosing list? */
1086
	if (NULL != n->parent->next)
1087
		mid_it();
1088
}
1089
1090
static int
1091
pre_em(DECL_ARGS)
1092
{
1093
1094
	font_push('I');
1095
	return 1;
1096
}
1097
1098
static int
1099
pre_en(DECL_ARGS)
1100
{
1101
1102
	if (NULL == n->norm->Es ||
1103
	    NULL == n->norm->Es->child)
1104
		return 1;
1105
1106
	print_word(n->norm->Es->child->string);
1107
	outflags &= ~MMAN_spc;
1108
	return 1;
1109
}
1110
1111
static void
1112
post_en(DECL_ARGS)
1113
{
1114
1115
	if (NULL == n->norm->Es ||
1116
	    NULL == n->norm->Es->child ||
1117
	    NULL == n->norm->Es->child->next)
1118
		return;
1119
1120
	outflags &= ~MMAN_spc;
1121
	print_word(n->norm->Es->child->next->string);
1122
	return;
1123
}
1124
1125
static int
1126
pre_eo(DECL_ARGS)
1127
{
1128
1129
	if (n->end == ENDBODY_NOT &&
1130
	    n->parent->head->child == NULL &&
1131
	    n->child != NULL &&
1132
	    n->child->end != ENDBODY_NOT)
1133
		print_word("\\&");
1134
	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1135
	    n->parent->head->child != NULL && (n->child != NULL ||
1136
	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1137
		outflags &= ~(MMAN_spc | MMAN_nl);
1138
	return 1;
1139
}
1140
1141
static void
1142
post_eo(DECL_ARGS)
1143
{
1144
	int	 body, tail;
1145
1146
	if (n->end != ENDBODY_NOT) {
1147
		outflags |= MMAN_spc;
1148
		return;
1149
	}
1150
1151
	body = n->child != NULL || n->parent->head->child != NULL;
1152
	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1153
1154
	if (body && tail)
1155
		outflags &= ~MMAN_spc;
1156
	else if ( ! (body || tail))
1157
		print_word("\\&");
1158
	else if ( ! tail)
1159
		outflags |= MMAN_spc;
1160
}
1161
1162
static int
1163
pre_fa(DECL_ARGS)
1164
{
1165
	int	 am_Fa;
1166
1167
	am_Fa = MDOC_Fa == n->tok;
1168
1169
	if (am_Fa)
1170
		n = n->child;
1171
1172
	while (NULL != n) {
1173
		font_push('I');
1174
		if (am_Fa || MDOC_SYNPRETTY & n->flags)
1175
			outflags |= MMAN_nbrword;
1176
		print_node(meta, n);
1177
		font_pop();
1178
		if (NULL != (n = n->next))
1179
			print_word(",");
1180
	}
1181
	return 0;
1182
}
1183
1184
static void
1185
post_fa(DECL_ARGS)
1186
{
1187
1188
	if (NULL != n->next && MDOC_Fa == n->next->tok)
1189
		print_word(",");
1190
}
1191
1192
static int
1193
pre_fd(DECL_ARGS)
1194
{
1195
1196
	pre_syn(n);
1197
	font_push('B');
1198
	return 1;
1199
}
1200
1201
static void
1202
post_fd(DECL_ARGS)
1203
{
1204
1205
	font_pop();
1206
	outflags |= MMAN_br;
1207
}
1208
1209
static int
1210
pre_fl(DECL_ARGS)
1211
{
1212
1213
	font_push('B');
1214
	print_word("\\-");
1215
	if (n->child != NULL)
1216
		outflags &= ~MMAN_spc;
1217
	return 1;
1218
}
1219
1220
static void
1221
post_fl(DECL_ARGS)
1222
{
1223
1224
	font_pop();
1225
	if (!(n->child != NULL ||
1226
	    n->next == NULL ||
1227
	    n->next->type == ROFFT_TEXT ||
1228
	    n->next->flags & MDOC_LINE))
1229
		outflags &= ~MMAN_spc;
1230
}
1231
1232
static int
1233
pre_fn(DECL_ARGS)
1234
{
1235
1236
	pre_syn(n);
1237
1238
	n = n->child;
1239
	if (NULL == n)
1240
		return 0;
1241
1242
	if (MDOC_SYNPRETTY & n->flags)
1243
		print_block(".HP 4n", MMAN_nl);
1244
1245
	font_push('B');
1246
	print_node(meta, n);
1247
	font_pop();
1248
	outflags &= ~MMAN_spc;
1249
	print_word("(");
1250
	outflags &= ~MMAN_spc;
1251
1252
	n = n->next;
1253
	if (NULL != n)
1254
		pre_fa(meta, n);
1255
	return 0;
1256
}
1257
1258
static void
1259
post_fn(DECL_ARGS)
1260
{
1261
1262
	print_word(")");
1263
	if (MDOC_SYNPRETTY & n->flags) {
1264
		print_word(";");
1265
		outflags |= MMAN_PP;
1266
	}
1267
}
1268
1269
static int
1270
pre_fo(DECL_ARGS)
1271
{
1272
1273
	switch (n->type) {
1274
	case ROFFT_BLOCK:
1275
		pre_syn(n);
1276
		break;
1277
	case ROFFT_HEAD:
1278
		if (n->child == NULL)
1279
			return 0;
1280
		if (MDOC_SYNPRETTY & n->flags)
1281
			print_block(".HP 4n", MMAN_nl);
1282
		font_push('B');
1283
		break;
1284
	case ROFFT_BODY:
1285
		outflags &= ~(MMAN_spc | MMAN_nl);
1286
		print_word("(");
1287
		outflags &= ~MMAN_spc;
1288
		break;
1289
	default:
1290
		break;
1291
	}
1292
	return 1;
1293
}
1294
1295
static void
1296
post_fo(DECL_ARGS)
1297
{
1298
1299
	switch (n->type) {
1300
	case ROFFT_HEAD:
1301
		if (n->child != NULL)
1302
			font_pop();
1303
		break;
1304
	case ROFFT_BODY:
1305
		post_fn(meta, n);
1306
		break;
1307
	default:
1308
		break;
1309
	}
1310
}
1311
1312
static int
1313
pre_ft(DECL_ARGS)
1314
{
1315
1316
	pre_syn(n);
1317
	font_push('I');
1318
	return 1;
1319
}
1320
1321
static int
1322
pre_in(DECL_ARGS)
1323
{
1324
1325
	if (MDOC_SYNPRETTY & n->flags) {
1326
		pre_syn(n);
1327
		font_push('B');
1328
		print_word("#include <");
1329
		outflags &= ~MMAN_spc;
1330
	} else {
1331
		print_word("<");
1332
		outflags &= ~MMAN_spc;
1333
		font_push('I');
1334
	}
1335
	return 1;
1336
}
1337
1338
static void
1339
post_in(DECL_ARGS)
1340
{
1341
1342
	if (MDOC_SYNPRETTY & n->flags) {
1343
		outflags &= ~MMAN_spc;
1344
		print_word(">");
1345
		font_pop();
1346
		outflags |= MMAN_br;
1347
	} else {
1348
		font_pop();
1349
		outflags &= ~MMAN_spc;
1350
		print_word(">");
1351
	}
1352
}
1353
1354
static int
1355
pre_it(DECL_ARGS)
1356
{
1357
	const struct roff_node *bln;
1358
1359
	switch (n->type) {
1360
	case ROFFT_HEAD:
1361
		outflags |= MMAN_PP | MMAN_nl;
1362
		bln = n->parent->parent;
1363
		if (0 == bln->norm->Bl.comp ||
1364
		    (NULL == n->parent->prev &&
1365
		     NULL == bln->parent->prev))
1366
			outflags |= MMAN_sp;
1367
		outflags &= ~MMAN_br;
1368
		switch (bln->norm->Bl.type) {
1369
		case LIST_item:
1370
			return 0;
1371
		case LIST_inset:
1372
		case LIST_diag:
1373
		case LIST_ohang:
1374
			if (bln->norm->Bl.type == LIST_diag)
1375
				print_line(".B \"", 0);
1376
			else
1377
				print_line(".R \"", 0);
1378
			outflags &= ~MMAN_spc;
1379
			return 1;
1380
		case LIST_bullet:
1381
		case LIST_dash:
1382
		case LIST_hyphen:
1383
			print_width(&bln->norm->Bl, NULL);
1384
			TPremain = 0;
1385
			outflags |= MMAN_nl;
1386
			font_push('B');
1387
			if (LIST_bullet == bln->norm->Bl.type)
1388
				print_word("\\(bu");
1389
			else
1390
				print_word("-");
1391
			font_pop();
1392
			outflags |= MMAN_nl;
1393
			return 0;
1394
		case LIST_enum:
1395
			print_width(&bln->norm->Bl, NULL);
1396
			TPremain = 0;
1397
			outflags |= MMAN_nl;
1398
			print_count(&bln->norm->Bl.count);
1399
			outflags |= MMAN_nl;
1400
			return 0;
1401
		case LIST_hang:
1402
			print_width(&bln->norm->Bl, n->child);
1403
			TPremain = 0;
1404
			outflags |= MMAN_nl;
1405
			return 1;
1406
		case LIST_tag:
1407
			print_width(&bln->norm->Bl, n->child);
1408
			putchar('\n');
1409
			outflags &= ~MMAN_spc;
1410
			return 1;
1411
		default:
1412
			return 1;
1413
		}
1414
	default:
1415
		break;
1416
	}
1417
	return 1;
1418
}
1419
1420
/*
1421
 * This function is called after closing out an indented block.
1422
 * If we are inside an enclosing list, restore its indentation.
1423
 */
1424
static void
1425
mid_it(void)
1426
{
1427
	char		 buf[24];
1428
1429
	/* Nothing to do outside a list. */
1430
	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1431
		return;
1432
1433
	/* The indentation has already been set up. */
1434
	if (Bl_stack_post[Bl_stack_len - 1])
1435
		return;
1436
1437
	/* Restore the indentation of the enclosing list. */
1438
	print_line(".RS", MMAN_Bk_susp);
1439
	(void)snprintf(buf, sizeof(buf), "%dn",
1440
	    Bl_stack[Bl_stack_len - 1]);
1441
	print_word(buf);
1442
1443
	/* Remeber to close out this .RS block later. */
1444
	Bl_stack_post[Bl_stack_len - 1] = 1;
1445
}
1446
1447
static void
1448
post_it(DECL_ARGS)
1449
{
1450
	const struct roff_node *bln;
1451
1452
	bln = n->parent->parent;
1453
1454
	switch (n->type) {
1455
	case ROFFT_HEAD:
1456
		switch (bln->norm->Bl.type) {
1457
		case LIST_diag:
1458
			outflags &= ~MMAN_spc;
1459
			print_word("\\ ");
1460
			break;
1461
		case LIST_ohang:
1462
			outflags |= MMAN_br;
1463
			break;
1464
		default:
1465
			break;
1466
		}
1467
		break;
1468
	case ROFFT_BODY:
1469
		switch (bln->norm->Bl.type) {
1470
		case LIST_bullet:
1471
		case LIST_dash:
1472
		case LIST_hyphen:
1473
		case LIST_enum:
1474
		case LIST_hang:
1475
		case LIST_tag:
1476
			assert(Bl_stack_len);
1477
			Bl_stack[--Bl_stack_len] = 0;
1478
1479
			/*
1480
			 * Our indentation had to be restored
1481
			 * after a child display or child list.
1482
			 * Close out that indentation block now.
1483
			 */
1484
			if (Bl_stack_post[Bl_stack_len]) {
1485
				print_line(".RE", MMAN_nl);
1486
				Bl_stack_post[Bl_stack_len] = 0;
1487
			}
1488
			break;
1489
		case LIST_column:
1490
			if (NULL != n->next) {
1491
				putchar('\t');
1492
				outflags &= ~MMAN_spc;
1493
			}
1494
			break;
1495
		default:
1496
			break;
1497
		}
1498
		break;
1499
	default:
1500
		break;
1501
	}
1502
}
1503
1504
static void
1505
post_lb(DECL_ARGS)
1506
{
1507
1508
	if (SEC_LIBRARY == n->sec)
1509
		outflags |= MMAN_br;
1510
}
1511
1512
static int
1513
pre_lk(DECL_ARGS)
1514
{
1515
	const struct roff_node *link, *descr;
1516
1517
	if (NULL == (link = n->child))
1518
		return 0;
1519
1520
	if (NULL != (descr = link->next)) {
1521
		font_push('I');
1522
		while (NULL != descr) {
1523
			print_word(descr->string);
1524
			descr = descr->next;
1525
		}
1526
		print_word(":");
1527
		font_pop();
1528
	}
1529
1530
	font_push('B');
1531
	print_word(link->string);
1532
	font_pop();
1533
	return 0;
1534
}
1535
1536
static int
1537
pre_ll(DECL_ARGS)
1538
{
1539
1540
	print_line(".ll", 0);
1541
	return 1;
1542
}
1543
1544
static int
1545
pre_li(DECL_ARGS)
1546
{
1547
1548
	font_push('R');
1549
	return 1;
1550
}
1551
1552
static int
1553
pre_nm(DECL_ARGS)
1554
{
1555
	char	*name;
1556
1557
	if (n->type == ROFFT_BLOCK) {
1558
		outflags |= MMAN_Bk;
1559
		pre_syn(n);
1560
	}
1561
	if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD)
1562
		return 1;
1563
	name = n->child ? n->child->string : meta->name;
1564
	if (NULL == name)
1565
		return 0;
1566
	if (n->type == ROFFT_HEAD) {
1567
		if (NULL == n->parent->prev)
1568
			outflags |= MMAN_sp;
1569
		print_block(".HP", 0);
1570
		printf(" %zun", strlen(name) + 1);
1571
		outflags |= MMAN_nl;
1572
	}
1573
	font_push('B');
1574
	if (NULL == n->child)
1575
		print_word(meta->name);
1576
	return 1;
1577
}
1578
1579
static void
1580
post_nm(DECL_ARGS)
1581
{
1582
1583
	switch (n->type) {
1584
	case ROFFT_BLOCK:
1585
		outflags &= ~MMAN_Bk;
1586
		break;
1587
	case ROFFT_HEAD:
1588
	case ROFFT_ELEM:
1589
		if (n->child != NULL || meta->name != NULL)
1590
			font_pop();
1591
		break;
1592
	default:
1593
		break;
1594
	}
1595
}
1596
1597
static int
1598
pre_no(DECL_ARGS)
1599
{
1600
1601
	outflags |= MMAN_spc_force;
1602
	return 1;
1603
}
1604
1605
static int
1606
pre_ns(DECL_ARGS)
1607
{
1608
1609
	outflags &= ~MMAN_spc;
1610
	return 0;
1611
}
1612
1613
static void
1614
post_pf(DECL_ARGS)
1615
{
1616
1617
	if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
1618
		outflags &= ~MMAN_spc;
1619
}
1620
1621
static int
1622
pre_pp(DECL_ARGS)
1623
{
1624
1625
	if (MDOC_It != n->parent->tok)
1626
		outflags |= MMAN_PP;
1627
	outflags |= MMAN_sp | MMAN_nl;
1628
	outflags &= ~MMAN_br;
1629
	return 0;
1630
}
1631
1632
static int
1633
pre_rs(DECL_ARGS)
1634
{
1635
1636
	if (SEC_SEE_ALSO == n->sec) {
1637
		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1638
		outflags &= ~MMAN_br;
1639
	}
1640
	return 1;
1641
}
1642
1643
static int
1644
pre_rv(DECL_ARGS)
1645
{
1646
	struct roff_node *nch;
1647
1648
	outflags |= MMAN_br | MMAN_nl;
1649
1650
	if (n->child != NULL) {
1651
		print_word("The");
1652
1653
		for (nch = n->child; nch != NULL; nch = nch->next) {
1654
			font_push('B');
1655
			print_word(nch->string);
1656
			font_pop();
1657
1658
			outflags &= ~MMAN_spc;
1659
			print_word("()");
1660
1661
			if (nch->next == NULL)
1662
				continue;
1663
1664
			if (nch->prev != NULL || nch->next->next != NULL) {
1665
				outflags &= ~MMAN_spc;
1666
				print_word(",");
1667
			}
1668
			if (nch->next->next == NULL)
1669
				print_word("and");
1670
		}
1671
1672
		if (n->child != NULL && n->child->next != NULL)
1673
			print_word("functions return");
1674
		else
1675
			print_word("function returns");
1676
1677
		print_word("the value\\~0 if successful;");
1678
	} else
1679
		print_word("Upon successful completion, "
1680
		    "the value\\~0 is returned;");
1681
1682
	print_word("otherwise the value\\~\\-1 is returned"
1683
	    " and the global variable");
1684
1685
	font_push('I');
1686
	print_word("errno");
1687
	font_pop();
1688
1689
	print_word("is set to indicate the error.");
1690
	outflags |= MMAN_nl;
1691
	return 0;
1692
}
1693
1694
static int
1695
pre_skip(DECL_ARGS)
1696
{
1697
1698
	return 0;
1699
}
1700
1701
static int
1702
pre_sm(DECL_ARGS)
1703
{
1704
1705
	if (NULL == n->child)
1706
		outflags ^= MMAN_Sm;
1707
	else if (0 == strcmp("on", n->child->string))
1708
		outflags |= MMAN_Sm;
1709
	else
1710
		outflags &= ~MMAN_Sm;
1711
1712
	if (MMAN_Sm & outflags)
1713
		outflags |= MMAN_spc;
1714
1715
	return 0;
1716
}
1717
1718
static int
1719
pre_sp(DECL_ARGS)
1720
{
1721
1722
	if (MMAN_PP & outflags) {
1723
		outflags &= ~MMAN_PP;
1724
		print_line(".PP", 0);
1725
	} else
1726
		print_line(".sp", 0);
1727
	return 1;
1728
}
1729
1730
static void
1731
post_sp(DECL_ARGS)
1732
{
1733
1734
	outflags |= MMAN_nl;
1735
}
1736
1737
static int
1738
pre_sy(DECL_ARGS)
1739
{
1740
1741
	font_push('B');
1742
	return 1;
1743
}
1744
1745
static int
1746
pre_vt(DECL_ARGS)
1747
{
1748
1749
	if (MDOC_SYNPRETTY & n->flags) {
1750
		switch (n->type) {
1751
		case ROFFT_BLOCK:
1752
			pre_syn(n);
1753
			return 1;
1754
		case ROFFT_BODY:
1755
			break;
1756
		default:
1757
			return 0;
1758
		}
1759
	}
1760
	font_push('I');
1761
	return 1;
1762
}
1763
1764
static void
1765
post_vt(DECL_ARGS)
1766
{
1767
1768
	if (n->flags & MDOC_SYNPRETTY && n->type != ROFFT_BODY)
1769
		return;
1770
	font_pop();
1771
}
1772
1773
static int
1774
pre_xr(DECL_ARGS)
1775
{
1776
1777
	n = n->child;
1778
	if (NULL == n)
1779
		return 0;
1780
	print_node(meta, n);
1781
	n = n->next;
1782
	if (NULL == n)
1783
		return 0;
1784
	outflags &= ~MMAN_spc;
1785
	print_word("(");
1786
	print_node(meta, n);
1787
	print_word(")");
1788
	return 0;
1789
}
1790
1791
static int
1792
pre_ux(DECL_ARGS)
1793
{
1794
1795
	print_word(manacts[n->tok].prefix);
1796
	if (NULL == n->child)
1797
		return 0;
1798
	outflags &= ~MMAN_spc;
1799
	print_word("\\ ");
1800
	outflags &= ~MMAN_spc;
1801
	return 1;
1802
}