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