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