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