xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision 5b859c19fe53bbea08f5c342e0a4470e99f883e1)
1 /*	$OpenBSD: mdoc_man.c,v 1.75 2014/11/30 05:28:00 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011, 2012, 2013, 2014 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.h"
24 #include "mandoc_aux.h"
25 #include "out.h"
26 #include "man.h"
27 #include "mdoc.h"
28 #include "main.h"
29 
30 #define	DECL_ARGS const struct mdoc_meta *meta, \
31 		  const struct mdoc_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 mdoc_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 char *,
118 				const struct mdoc_node *, size_t);
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	size_t		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 	size_t		  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_MAX)) {
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), "%zun", 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 char *v, const struct mdoc_node *child, size_t defsz)
470 {
471 	char		  buf[24];
472 	struct roffsu	  su;
473 	size_t		  sz, chsz;
474 	int		  numeric, remain;
475 
476 	numeric = 1;
477 	remain = 0;
478 
479 	/* Convert v into a number (of characters). */
480 	if (NULL == v)
481 		sz = defsz;
482 	else if (a2roffsu(v, &su, SCALE_MAX)) {
483 		if (SCALE_EN == su.unit)
484 			sz = su.scale;
485 		else {
486 			sz = 0;
487 			numeric = 0;
488 		}
489 	} else
490 		sz = strlen(v);
491 
492 	/* XXX Rough estimation, might have multiple parts. */
493 	chsz = (NULL != child && MDOC_TEXT == child->type) ?
494 	    strlen(child->string) : 0;
495 
496 	/* Maybe we are inside an enclosing list? */
497 	mid_it();
498 
499 	/*
500 	 * Save our own indentation,
501 	 * such that child lists can use it.
502 	 */
503 	Bl_stack[Bl_stack_len++] = sz + 2;
504 
505 	/* Set up the current list. */
506 	if (defsz && chsz > sz)
507 		print_block(".HP", 0);
508 	else {
509 		print_block(".TP", 0);
510 		remain = sz + 2;
511 	}
512 	if (numeric) {
513 		(void)snprintf(buf, sizeof(buf), "%zun", sz + 2);
514 		print_word(buf);
515 	} else
516 		print_word(v);
517 	TPremain = remain;
518 }
519 
520 static void
521 print_count(int *count)
522 {
523 	char		  buf[24];
524 
525 	(void)snprintf(buf, sizeof(buf), "%d.", ++*count);
526 	print_word(buf);
527 }
528 
529 void
530 man_man(void *arg, const struct man *man)
531 {
532 
533 	/*
534 	 * Dump the keep buffer.
535 	 * We're guaranteed by now that this exists (is non-NULL).
536 	 * Flush stdout afterward, just in case.
537 	 */
538 	fputs(mparse_getkeep(man_mparse(man)), stdout);
539 	fflush(stdout);
540 }
541 
542 void
543 man_mdoc(void *arg, const struct mdoc *mdoc)
544 {
545 	const struct mdoc_meta *meta;
546 	const struct mdoc_node *n;
547 
548 	meta = mdoc_meta(mdoc);
549 	n = mdoc_node(mdoc);
550 
551 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
552 	    meta->title,
553 	    (meta->msec == NULL ? "" : meta->msec),
554 	    meta->date, meta->os, meta->vol);
555 
556 	/* Disable hyphenation and if nroff, disable justification. */
557 	printf(".nh\n.if n .ad l");
558 
559 	outflags = MMAN_nl | MMAN_Sm;
560 	if (0 == fontqueue.size) {
561 		fontqueue.size = 8;
562 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
563 		*fontqueue.tail = 'R';
564 	}
565 	print_node(meta, n);
566 	putchar('\n');
567 }
568 
569 static void
570 print_node(DECL_ARGS)
571 {
572 	const struct mdoc_node	*sub;
573 	const struct manact	*act;
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 
587 	if (MDOC_TEXT == n->type) {
588 		/*
589 		 * Make sure that we don't happen to start with a
590 		 * control character at the start of a line.
591 		 */
592 		if (MMAN_nl & outflags &&
593 		    ('.' == *n->string || '\'' == *n->string)) {
594 			print_word("");
595 			printf("\\&");
596 			outflags &= ~MMAN_spc;
597 		}
598 		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMC))
599 			outflags |= MMAN_spc_force;
600 		print_word(n->string);
601 		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMO))
602 			outflags |= MMAN_spc;
603 	} else {
604 		/*
605 		 * Conditionally run the pre-node action handler for a
606 		 * node.
607 		 */
608 		act = manacts + n->tok;
609 		cond = act->cond == NULL || (*act->cond)(meta, n);
610 		if (cond && act->pre && (n->end == ENDBODY_NOT || n->nchild))
611 			do_sub = (*act->pre)(meta, n);
612 	}
613 
614 	/*
615 	 * Conditionally run all child nodes.
616 	 * Note that this iterates over children instead of using
617 	 * recursion.  This prevents unnecessary depth in the stack.
618 	 */
619 	if (do_sub)
620 		for (sub = n->child; sub; sub = sub->next)
621 			print_node(meta, sub);
622 
623 	/*
624 	 * Lastly, conditionally run the post-node handler.
625 	 */
626 	if (MDOC_ENDED & n->flags)
627 		return;
628 
629 	if (cond && act->post)
630 		(*act->post)(meta, n);
631 
632 	if (ENDBODY_NOT != n->end)
633 		n->pending->flags |= MDOC_ENDED;
634 
635 	if (ENDBODY_NOSPACE == n->end)
636 		outflags &= ~(MMAN_spc | MMAN_nl);
637 }
638 
639 static int
640 cond_head(DECL_ARGS)
641 {
642 
643 	return(MDOC_HEAD == n->type);
644 }
645 
646 static int
647 cond_body(DECL_ARGS)
648 {
649 
650 	return(MDOC_BODY == n->type);
651 }
652 
653 static int
654 pre_enc(DECL_ARGS)
655 {
656 	const char	*prefix;
657 
658 	prefix = manacts[n->tok].prefix;
659 	if (NULL == prefix)
660 		return(1);
661 	print_word(prefix);
662 	outflags &= ~MMAN_spc;
663 	return(1);
664 }
665 
666 static void
667 post_enc(DECL_ARGS)
668 {
669 	const char *suffix;
670 
671 	suffix = manacts[n->tok].suffix;
672 	if (NULL == suffix)
673 		return;
674 	outflags &= ~(MMAN_spc | MMAN_nl);
675 	print_word(suffix);
676 }
677 
678 static int
679 pre_ex(DECL_ARGS)
680 {
681 	int	 nchild;
682 
683 	outflags |= MMAN_br | MMAN_nl;
684 
685 	print_word("The");
686 
687 	nchild = n->nchild;
688 	for (n = n->child; n; n = n->next) {
689 		font_push('B');
690 		print_word(n->string);
691 		font_pop();
692 
693 		if (n->next == NULL)
694 			continue;
695 
696 		if (nchild > 2) {
697 			outflags &= ~MMAN_spc;
698 			print_word(",");
699 		}
700 		if (n->next->next == NULL)
701 			print_word("and");
702 	}
703 
704 	if (nchild > 1)
705 		print_word("utilities exit\\~0");
706 	else
707 		print_word("utility exits\\~0");
708 
709 	print_word("on success, and\\~>0 if an error occurs.");
710 	outflags |= MMAN_nl;
711 	return(0);
712 }
713 
714 static void
715 post_font(DECL_ARGS)
716 {
717 
718 	font_pop();
719 }
720 
721 static void
722 post_percent(DECL_ARGS)
723 {
724 
725 	if (pre_em == manacts[n->tok].pre)
726 		font_pop();
727 	if (n->next) {
728 		print_word(",");
729 		if (n->prev &&	n->prev->tok == n->tok &&
730 				n->next->tok == n->tok)
731 			print_word("and");
732 	} else {
733 		print_word(".");
734 		outflags |= MMAN_nl;
735 	}
736 }
737 
738 static int
739 pre__t(DECL_ARGS)
740 {
741 
742 	if (n->parent && MDOC_Rs == n->parent->tok &&
743 	    n->parent->norm->Rs.quote_T) {
744 		print_word("");
745 		putchar('\"');
746 		outflags &= ~MMAN_spc;
747 	} else
748 		font_push('I');
749 	return(1);
750 }
751 
752 static void
753 post__t(DECL_ARGS)
754 {
755 
756 	if (n->parent && MDOC_Rs == n->parent->tok &&
757 	    n->parent->norm->Rs.quote_T) {
758 		outflags &= ~MMAN_spc;
759 		print_word("");
760 		putchar('\"');
761 	} else
762 		font_pop();
763 	post_percent(meta, n);
764 }
765 
766 /*
767  * Print before a section header.
768  */
769 static int
770 pre_sect(DECL_ARGS)
771 {
772 
773 	if (MDOC_HEAD == n->type) {
774 		outflags |= MMAN_sp;
775 		print_block(manacts[n->tok].prefix, 0);
776 		print_word("");
777 		putchar('\"');
778 		outflags &= ~MMAN_spc;
779 	}
780 	return(1);
781 }
782 
783 /*
784  * Print subsequent a section header.
785  */
786 static void
787 post_sect(DECL_ARGS)
788 {
789 
790 	if (MDOC_HEAD != n->type)
791 		return;
792 	outflags &= ~MMAN_spc;
793 	print_word("");
794 	putchar('\"');
795 	outflags |= MMAN_nl;
796 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
797 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
798 }
799 
800 /* See mdoc_term.c, synopsis_pre() for comments. */
801 static void
802 pre_syn(const struct mdoc_node *n)
803 {
804 
805 	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
806 		return;
807 
808 	if (n->prev->tok == n->tok &&
809 	    MDOC_Ft != n->tok &&
810 	    MDOC_Fo != n->tok &&
811 	    MDOC_Fn != n->tok) {
812 		outflags |= MMAN_br;
813 		return;
814 	}
815 
816 	switch (n->prev->tok) {
817 	case MDOC_Fd:
818 		/* FALLTHROUGH */
819 	case MDOC_Fn:
820 		/* FALLTHROUGH */
821 	case MDOC_Fo:
822 		/* FALLTHROUGH */
823 	case MDOC_In:
824 		/* FALLTHROUGH */
825 	case MDOC_Vt:
826 		outflags |= MMAN_sp;
827 		break;
828 	case MDOC_Ft:
829 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
830 			outflags |= MMAN_sp;
831 			break;
832 		}
833 		/* FALLTHROUGH */
834 	default:
835 		outflags |= MMAN_br;
836 		break;
837 	}
838 }
839 
840 static int
841 pre_an(DECL_ARGS)
842 {
843 
844 	switch (n->norm->An.auth) {
845 	case AUTH_split:
846 		outflags &= ~MMAN_An_nosplit;
847 		outflags |= MMAN_An_split;
848 		return(0);
849 	case AUTH_nosplit:
850 		outflags &= ~MMAN_An_split;
851 		outflags |= MMAN_An_nosplit;
852 		return(0);
853 	default:
854 		if (MMAN_An_split & outflags)
855 			outflags |= MMAN_br;
856 		else if (SEC_AUTHORS == n->sec &&
857 		    ! (MMAN_An_nosplit & outflags))
858 			outflags |= MMAN_An_split;
859 		return(1);
860 	}
861 }
862 
863 static int
864 pre_ap(DECL_ARGS)
865 {
866 
867 	outflags &= ~MMAN_spc;
868 	print_word("'");
869 	outflags &= ~MMAN_spc;
870 	return(0);
871 }
872 
873 static int
874 pre_aq(DECL_ARGS)
875 {
876 
877 	print_word(n->parent->prev != NULL &&
878 	    n->parent->prev->tok == MDOC_An ?  "<" : "\\(la");
879 	outflags &= ~MMAN_spc;
880 	return(1);
881 }
882 
883 static void
884 post_aq(DECL_ARGS)
885 {
886 
887 	outflags &= ~(MMAN_spc | MMAN_nl);
888 	print_word(n->parent->prev != NULL &&
889 	    n->parent->prev->tok == MDOC_An ?  ">" : "\\(ra");
890 }
891 
892 static int
893 pre_bd(DECL_ARGS)
894 {
895 
896 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
897 
898 	if (DISP_unfilled == n->norm->Bd.type ||
899 	    DISP_literal  == n->norm->Bd.type)
900 		print_line(".nf", 0);
901 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
902 		outflags |= MMAN_sp;
903 	print_offs(n->norm->Bd.offs, 1);
904 	return(1);
905 }
906 
907 static void
908 post_bd(DECL_ARGS)
909 {
910 
911 	/* Close out this display. */
912 	print_line(".RE", MMAN_nl);
913 	if (DISP_unfilled == n->norm->Bd.type ||
914 	    DISP_literal  == n->norm->Bd.type)
915 		print_line(".fi", MMAN_nl);
916 
917 	/* Maybe we are inside an enclosing list? */
918 	if (NULL != n->parent->next)
919 		mid_it();
920 }
921 
922 static int
923 pre_bf(DECL_ARGS)
924 {
925 
926 	switch (n->type) {
927 	case MDOC_BLOCK:
928 		return(1);
929 	case MDOC_BODY:
930 		break;
931 	default:
932 		return(0);
933 	}
934 	switch (n->norm->Bf.font) {
935 	case FONT_Em:
936 		font_push('I');
937 		break;
938 	case FONT_Sy:
939 		font_push('B');
940 		break;
941 	default:
942 		font_push('R');
943 		break;
944 	}
945 	return(1);
946 }
947 
948 static void
949 post_bf(DECL_ARGS)
950 {
951 
952 	if (MDOC_BODY == n->type)
953 		font_pop();
954 }
955 
956 static int
957 pre_bk(DECL_ARGS)
958 {
959 
960 	switch (n->type) {
961 	case MDOC_BLOCK:
962 		return(1);
963 	case MDOC_BODY:
964 		outflags |= MMAN_Bk;
965 		return(1);
966 	default:
967 		return(0);
968 	}
969 }
970 
971 static void
972 post_bk(DECL_ARGS)
973 {
974 
975 	if (MDOC_BODY == n->type)
976 		outflags &= ~MMAN_Bk;
977 }
978 
979 static int
980 pre_bl(DECL_ARGS)
981 {
982 	size_t		 icol;
983 
984 	/*
985 	 * print_offs() will increase the -offset to account for
986 	 * a possible enclosing .It, but any enclosed .It blocks
987 	 * just nest and do not add up their indentation.
988 	 */
989 	if (n->norm->Bl.offs) {
990 		print_offs(n->norm->Bl.offs, 0);
991 		Bl_stack[Bl_stack_len++] = 0;
992 	}
993 
994 	switch (n->norm->Bl.type) {
995 	case LIST_enum:
996 		n->norm->Bl.count = 0;
997 		return(1);
998 	case LIST_column:
999 		break;
1000 	default:
1001 		return(1);
1002 	}
1003 
1004 	print_line(".TS", MMAN_nl);
1005 	for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1006 		print_word("l");
1007 	print_word(".");
1008 	outflags |= MMAN_nl;
1009 	return(1);
1010 }
1011 
1012 static void
1013 post_bl(DECL_ARGS)
1014 {
1015 
1016 	switch (n->norm->Bl.type) {
1017 	case LIST_column:
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 	outflags &= ~(MMAN_spc | MMAN_nl);
1130 	return(1);
1131 }
1132 
1133 static void
1134 post_eo(DECL_ARGS)
1135 {
1136 
1137 	if (n->end != ENDBODY_SPACE)
1138 		outflags &= ~MMAN_spc;
1139 }
1140 
1141 static int
1142 pre_fa(DECL_ARGS)
1143 {
1144 	int	 am_Fa;
1145 
1146 	am_Fa = MDOC_Fa == n->tok;
1147 
1148 	if (am_Fa)
1149 		n = n->child;
1150 
1151 	while (NULL != n) {
1152 		font_push('I');
1153 		if (am_Fa || MDOC_SYNPRETTY & n->flags)
1154 			outflags |= MMAN_nbrword;
1155 		print_node(meta, n);
1156 		font_pop();
1157 		if (NULL != (n = n->next))
1158 			print_word(",");
1159 	}
1160 	return(0);
1161 }
1162 
1163 static void
1164 post_fa(DECL_ARGS)
1165 {
1166 
1167 	if (NULL != n->next && MDOC_Fa == n->next->tok)
1168 		print_word(",");
1169 }
1170 
1171 static int
1172 pre_fd(DECL_ARGS)
1173 {
1174 
1175 	pre_syn(n);
1176 	font_push('B');
1177 	return(1);
1178 }
1179 
1180 static void
1181 post_fd(DECL_ARGS)
1182 {
1183 
1184 	font_pop();
1185 	outflags |= MMAN_br;
1186 }
1187 
1188 static int
1189 pre_fl(DECL_ARGS)
1190 {
1191 
1192 	font_push('B');
1193 	print_word("\\-");
1194 	if (n->nchild)
1195 		outflags &= ~MMAN_spc;
1196 	return(1);
1197 }
1198 
1199 static void
1200 post_fl(DECL_ARGS)
1201 {
1202 
1203 	font_pop();
1204 	if ( ! (n->nchild ||
1205 	    n->next == NULL ||
1206 	    n->next->type == MDOC_TEXT ||
1207 	    n->next->flags & MDOC_LINE))
1208 		outflags &= ~MMAN_spc;
1209 }
1210 
1211 static int
1212 pre_fn(DECL_ARGS)
1213 {
1214 
1215 	pre_syn(n);
1216 
1217 	n = n->child;
1218 	if (NULL == n)
1219 		return(0);
1220 
1221 	if (MDOC_SYNPRETTY & n->flags)
1222 		print_block(".HP 4n", MMAN_nl);
1223 
1224 	font_push('B');
1225 	print_node(meta, n);
1226 	font_pop();
1227 	outflags &= ~MMAN_spc;
1228 	print_word("(");
1229 	outflags &= ~MMAN_spc;
1230 
1231 	n = n->next;
1232 	if (NULL != n)
1233 		pre_fa(meta, n);
1234 	return(0);
1235 }
1236 
1237 static void
1238 post_fn(DECL_ARGS)
1239 {
1240 
1241 	print_word(")");
1242 	if (MDOC_SYNPRETTY & n->flags) {
1243 		print_word(";");
1244 		outflags |= MMAN_PP;
1245 	}
1246 }
1247 
1248 static int
1249 pre_fo(DECL_ARGS)
1250 {
1251 
1252 	switch (n->type) {
1253 	case MDOC_BLOCK:
1254 		pre_syn(n);
1255 		break;
1256 	case MDOC_HEAD:
1257 		if (MDOC_SYNPRETTY & n->flags)
1258 			print_block(".HP 4n", MMAN_nl);
1259 		font_push('B');
1260 		break;
1261 	case MDOC_BODY:
1262 		outflags &= ~MMAN_spc;
1263 		print_word("(");
1264 		outflags &= ~MMAN_spc;
1265 		break;
1266 	default:
1267 		break;
1268 	}
1269 	return(1);
1270 }
1271 
1272 static void
1273 post_fo(DECL_ARGS)
1274 {
1275 
1276 	switch (n->type) {
1277 	case MDOC_HEAD:
1278 		font_pop();
1279 		break;
1280 	case MDOC_BODY:
1281 		post_fn(meta, n);
1282 		break;
1283 	default:
1284 		break;
1285 	}
1286 }
1287 
1288 static int
1289 pre_ft(DECL_ARGS)
1290 {
1291 
1292 	pre_syn(n);
1293 	font_push('I');
1294 	return(1);
1295 }
1296 
1297 static int
1298 pre_in(DECL_ARGS)
1299 {
1300 
1301 	if (MDOC_SYNPRETTY & n->flags) {
1302 		pre_syn(n);
1303 		font_push('B');
1304 		print_word("#include <");
1305 		outflags &= ~MMAN_spc;
1306 	} else {
1307 		print_word("<");
1308 		outflags &= ~MMAN_spc;
1309 		font_push('I');
1310 	}
1311 	return(1);
1312 }
1313 
1314 static void
1315 post_in(DECL_ARGS)
1316 {
1317 
1318 	if (MDOC_SYNPRETTY & n->flags) {
1319 		outflags &= ~MMAN_spc;
1320 		print_word(">");
1321 		font_pop();
1322 		outflags |= MMAN_br;
1323 	} else {
1324 		font_pop();
1325 		outflags &= ~MMAN_spc;
1326 		print_word(">");
1327 	}
1328 }
1329 
1330 static int
1331 pre_it(DECL_ARGS)
1332 {
1333 	const struct mdoc_node *bln;
1334 
1335 	switch (n->type) {
1336 	case MDOC_HEAD:
1337 		outflags |= MMAN_PP | MMAN_nl;
1338 		bln = n->parent->parent;
1339 		if (0 == bln->norm->Bl.comp ||
1340 		    (NULL == n->parent->prev &&
1341 		     NULL == bln->parent->prev))
1342 			outflags |= MMAN_sp;
1343 		outflags &= ~MMAN_br;
1344 		switch (bln->norm->Bl.type) {
1345 		case LIST_item:
1346 			return(0);
1347 		case LIST_inset:
1348 			/* FALLTHROUGH */
1349 		case LIST_diag:
1350 			/* FALLTHROUGH */
1351 		case LIST_ohang:
1352 			if (bln->norm->Bl.type == LIST_diag)
1353 				print_line(".B \"", 0);
1354 			else
1355 				print_line(".R \"", 0);
1356 			outflags &= ~MMAN_spc;
1357 			return(1);
1358 		case LIST_bullet:
1359 			/* FALLTHROUGH */
1360 		case LIST_dash:
1361 			/* FALLTHROUGH */
1362 		case LIST_hyphen:
1363 			print_width(bln->norm->Bl.width, NULL, 0);
1364 			TPremain = 0;
1365 			outflags |= MMAN_nl;
1366 			font_push('B');
1367 			if (LIST_bullet == bln->norm->Bl.type)
1368 				print_word("\\(bu");
1369 			else
1370 				print_word("-");
1371 			font_pop();
1372 			outflags |= MMAN_nl;
1373 			return(0);
1374 		case LIST_enum:
1375 			print_width(bln->norm->Bl.width, NULL, 0);
1376 			TPremain = 0;
1377 			outflags |= MMAN_nl;
1378 			print_count(&bln->norm->Bl.count);
1379 			outflags |= MMAN_nl;
1380 			return(0);
1381 		case LIST_hang:
1382 			print_width(bln->norm->Bl.width, n->child, 6);
1383 			TPremain = 0;
1384 			outflags |= MMAN_nl;
1385 			return(1);
1386 		case LIST_tag:
1387 			print_width(bln->norm->Bl.width, n->child, 0);
1388 			putchar('\n');
1389 			outflags &= ~MMAN_spc;
1390 			return(1);
1391 		default:
1392 			return(1);
1393 		}
1394 	default:
1395 		break;
1396 	}
1397 	return(1);
1398 }
1399 
1400 /*
1401  * This function is called after closing out an indented block.
1402  * If we are inside an enclosing list, restore its indentation.
1403  */
1404 static void
1405 mid_it(void)
1406 {
1407 	char		 buf[24];
1408 
1409 	/* Nothing to do outside a list. */
1410 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1411 		return;
1412 
1413 	/* The indentation has already been set up. */
1414 	if (Bl_stack_post[Bl_stack_len - 1])
1415 		return;
1416 
1417 	/* Restore the indentation of the enclosing list. */
1418 	print_line(".RS", MMAN_Bk_susp);
1419 	(void)snprintf(buf, sizeof(buf), "%zun",
1420 	    Bl_stack[Bl_stack_len - 1]);
1421 	print_word(buf);
1422 
1423 	/* Remeber to close out this .RS block later. */
1424 	Bl_stack_post[Bl_stack_len - 1] = 1;
1425 }
1426 
1427 static void
1428 post_it(DECL_ARGS)
1429 {
1430 	const struct mdoc_node *bln;
1431 
1432 	bln = n->parent->parent;
1433 
1434 	switch (n->type) {
1435 	case MDOC_HEAD:
1436 		switch (bln->norm->Bl.type) {
1437 		case LIST_diag:
1438 			outflags &= ~MMAN_spc;
1439 			print_word("\\ ");
1440 			break;
1441 		case LIST_ohang:
1442 			outflags |= MMAN_br;
1443 			break;
1444 		default:
1445 			break;
1446 		}
1447 		break;
1448 	case MDOC_BODY:
1449 		switch (bln->norm->Bl.type) {
1450 		case LIST_bullet:
1451 			/* FALLTHROUGH */
1452 		case LIST_dash:
1453 			/* FALLTHROUGH */
1454 		case LIST_hyphen:
1455 			/* FALLTHROUGH */
1456 		case LIST_enum:
1457 			/* FALLTHROUGH */
1458 		case LIST_hang:
1459 			/* FALLTHROUGH */
1460 		case LIST_tag:
1461 			assert(Bl_stack_len);
1462 			Bl_stack[--Bl_stack_len] = 0;
1463 
1464 			/*
1465 			 * Our indentation had to be restored
1466 			 * after a child display or child list.
1467 			 * Close out that indentation block now.
1468 			 */
1469 			if (Bl_stack_post[Bl_stack_len]) {
1470 				print_line(".RE", MMAN_nl);
1471 				Bl_stack_post[Bl_stack_len] = 0;
1472 			}
1473 			break;
1474 		case LIST_column:
1475 			if (NULL != n->next) {
1476 				putchar('\t');
1477 				outflags &= ~MMAN_spc;
1478 			}
1479 			break;
1480 		default:
1481 			break;
1482 		}
1483 		break;
1484 	default:
1485 		break;
1486 	}
1487 }
1488 
1489 static void
1490 post_lb(DECL_ARGS)
1491 {
1492 
1493 	if (SEC_LIBRARY == n->sec)
1494 		outflags |= MMAN_br;
1495 }
1496 
1497 static int
1498 pre_lk(DECL_ARGS)
1499 {
1500 	const struct mdoc_node *link, *descr;
1501 
1502 	if (NULL == (link = n->child))
1503 		return(0);
1504 
1505 	if (NULL != (descr = link->next)) {
1506 		font_push('I');
1507 		while (NULL != descr) {
1508 			print_word(descr->string);
1509 			descr = descr->next;
1510 		}
1511 		print_word(":");
1512 		font_pop();
1513 	}
1514 
1515 	font_push('B');
1516 	print_word(link->string);
1517 	font_pop();
1518 	return(0);
1519 }
1520 
1521 static int
1522 pre_ll(DECL_ARGS)
1523 {
1524 
1525 	print_line(".ll", 0);
1526 	return(1);
1527 }
1528 
1529 static int
1530 pre_li(DECL_ARGS)
1531 {
1532 
1533 	font_push('R');
1534 	return(1);
1535 }
1536 
1537 static int
1538 pre_nm(DECL_ARGS)
1539 {
1540 	char	*name;
1541 
1542 	if (MDOC_BLOCK == n->type) {
1543 		outflags |= MMAN_Bk;
1544 		pre_syn(n);
1545 	}
1546 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1547 		return(1);
1548 	name = n->child ? n->child->string : meta->name;
1549 	if (NULL == name)
1550 		return(0);
1551 	if (MDOC_HEAD == n->type) {
1552 		if (NULL == n->parent->prev)
1553 			outflags |= MMAN_sp;
1554 		print_block(".HP", 0);
1555 		printf(" %zun", strlen(name) + 1);
1556 		outflags |= MMAN_nl;
1557 	}
1558 	font_push('B');
1559 	if (NULL == n->child)
1560 		print_word(meta->name);
1561 	return(1);
1562 }
1563 
1564 static void
1565 post_nm(DECL_ARGS)
1566 {
1567 
1568 	switch (n->type) {
1569 	case MDOC_BLOCK:
1570 		outflags &= ~MMAN_Bk;
1571 		break;
1572 	case MDOC_HEAD:
1573 		/* FALLTHROUGH */
1574 	case MDOC_ELEM:
1575 		if (n->child != NULL || meta->name != NULL)
1576 			font_pop();
1577 		break;
1578 	default:
1579 		break;
1580 	}
1581 }
1582 
1583 static int
1584 pre_no(DECL_ARGS)
1585 {
1586 
1587 	outflags |= MMAN_spc_force;
1588 	return(1);
1589 }
1590 
1591 static int
1592 pre_ns(DECL_ARGS)
1593 {
1594 
1595 	outflags &= ~MMAN_spc;
1596 	return(0);
1597 }
1598 
1599 static void
1600 post_pf(DECL_ARGS)
1601 {
1602 
1603 	if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
1604 		outflags &= ~MMAN_spc;
1605 }
1606 
1607 static int
1608 pre_pp(DECL_ARGS)
1609 {
1610 
1611 	if (MDOC_It != n->parent->tok)
1612 		outflags |= MMAN_PP;
1613 	outflags |= MMAN_sp | MMAN_nl;
1614 	outflags &= ~MMAN_br;
1615 	return(0);
1616 }
1617 
1618 static int
1619 pre_rs(DECL_ARGS)
1620 {
1621 
1622 	if (SEC_SEE_ALSO == n->sec) {
1623 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1624 		outflags &= ~MMAN_br;
1625 	}
1626 	return(1);
1627 }
1628 
1629 static int
1630 pre_rv(DECL_ARGS)
1631 {
1632 	int	 nchild;
1633 
1634 	outflags |= MMAN_br | MMAN_nl;
1635 
1636 	nchild = n->nchild;
1637 	if (nchild > 0) {
1638 		print_word("The");
1639 
1640 		for (n = n->child; n; n = n->next) {
1641 			font_push('B');
1642 			print_word(n->string);
1643 			font_pop();
1644 
1645 			outflags &= ~MMAN_spc;
1646 			print_word("()");
1647 
1648 			if (n->next == NULL)
1649 				continue;
1650 
1651 			if (nchild > 2) {
1652 				outflags &= ~MMAN_spc;
1653 				print_word(",");
1654 			}
1655 			if (n->next->next == NULL)
1656 				print_word("and");
1657 		}
1658 
1659 		if (nchild > 1)
1660 			print_word("functions return");
1661 		else
1662 			print_word("function returns");
1663 
1664 		print_word("the value\\~0 if successful;");
1665 	} else
1666 		print_word("Upon successful completion, "
1667 		    "the value\\~0 is returned;");
1668 
1669 	print_word("otherwise the value\\~\\-1 is returned"
1670 	    " and the global variable");
1671 
1672 	font_push('I');
1673 	print_word("errno");
1674 	font_pop();
1675 
1676 	print_word("is set to indicate the error.");
1677 	outflags |= MMAN_nl;
1678 	return(0);
1679 }
1680 
1681 static int
1682 pre_skip(DECL_ARGS)
1683 {
1684 
1685 	return(0);
1686 }
1687 
1688 static int
1689 pre_sm(DECL_ARGS)
1690 {
1691 
1692 	if (NULL == n->child)
1693 		outflags ^= MMAN_Sm;
1694 	else if (0 == strcmp("on", n->child->string))
1695 		outflags |= MMAN_Sm;
1696 	else
1697 		outflags &= ~MMAN_Sm;
1698 
1699 	if (MMAN_Sm & outflags)
1700 		outflags |= MMAN_spc;
1701 
1702 	return(0);
1703 }
1704 
1705 static int
1706 pre_sp(DECL_ARGS)
1707 {
1708 
1709 	if (MMAN_PP & outflags) {
1710 		outflags &= ~MMAN_PP;
1711 		print_line(".PP", 0);
1712 	} else
1713 		print_line(".sp", 0);
1714 	return(1);
1715 }
1716 
1717 static void
1718 post_sp(DECL_ARGS)
1719 {
1720 
1721 	outflags |= MMAN_nl;
1722 }
1723 
1724 static int
1725 pre_sy(DECL_ARGS)
1726 {
1727 
1728 	font_push('B');
1729 	return(1);
1730 }
1731 
1732 static int
1733 pre_vt(DECL_ARGS)
1734 {
1735 
1736 	if (MDOC_SYNPRETTY & n->flags) {
1737 		switch (n->type) {
1738 		case MDOC_BLOCK:
1739 			pre_syn(n);
1740 			return(1);
1741 		case MDOC_BODY:
1742 			break;
1743 		default:
1744 			return(0);
1745 		}
1746 	}
1747 	font_push('I');
1748 	return(1);
1749 }
1750 
1751 static void
1752 post_vt(DECL_ARGS)
1753 {
1754 
1755 	if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1756 		return;
1757 	font_pop();
1758 }
1759 
1760 static int
1761 pre_xr(DECL_ARGS)
1762 {
1763 
1764 	n = n->child;
1765 	if (NULL == n)
1766 		return(0);
1767 	print_node(meta, n);
1768 	n = n->next;
1769 	if (NULL == n)
1770 		return(0);
1771 	outflags &= ~MMAN_spc;
1772 	print_word("(");
1773 	print_node(meta, n);
1774 	print_word(")");
1775 	return(0);
1776 }
1777 
1778 static int
1779 pre_ux(DECL_ARGS)
1780 {
1781 
1782 	print_word(manacts[n->tok].prefix);
1783 	if (NULL == n->child)
1784 		return(0);
1785 	outflags &= ~MMAN_spc;
1786 	print_word("\\ ");
1787 	outflags &= ~MMAN_spc;
1788 	return(1);
1789 }
1790