xref: /openbsd/usr.bin/mandoc/mdoc_man.c (revision 9b7c3dbb)
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 }
1803