xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision b33a8d55775a3013157d6b0e3602c1fe178b48a6)
1 /*	$OpenBSD: mdoc_man.c,v 1.91 2015/04/18 17:50:02 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011-2015 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 && (n->end == ENDBODY_NOT || n->nchild))
612 			do_sub = (*act->pre)(meta, n);
613 	}
614 
615 	/*
616 	 * Conditionally run all child nodes.
617 	 * Note that this iterates over children instead of using
618 	 * recursion.  This prevents unnecessary depth in the stack.
619 	 */
620 	if (do_sub)
621 		for (sub = n->child; sub; sub = sub->next)
622 			print_node(meta, sub);
623 
624 	/*
625 	 * Lastly, conditionally run the post-node handler.
626 	 */
627 	if (MDOC_ENDED & n->flags)
628 		return;
629 
630 	if (cond && act->post)
631 		(*act->post)(meta, n);
632 
633 	if (ENDBODY_NOT != n->end)
634 		n->body->flags |= MDOC_ENDED;
635 
636 	if (ENDBODY_NOSPACE == n->end)
637 		outflags &= ~(MMAN_spc | MMAN_nl);
638 }
639 
640 static int
641 cond_head(DECL_ARGS)
642 {
643 
644 	return(n->type == ROFFT_HEAD);
645 }
646 
647 static int
648 cond_body(DECL_ARGS)
649 {
650 
651 	return(n->type == ROFFT_BODY);
652 }
653 
654 static int
655 pre_enc(DECL_ARGS)
656 {
657 	const char	*prefix;
658 
659 	prefix = manacts[n->tok].prefix;
660 	if (NULL == prefix)
661 		return(1);
662 	print_word(prefix);
663 	outflags &= ~MMAN_spc;
664 	return(1);
665 }
666 
667 static void
668 post_enc(DECL_ARGS)
669 {
670 	const char *suffix;
671 
672 	suffix = manacts[n->tok].suffix;
673 	if (NULL == suffix)
674 		return;
675 	outflags &= ~(MMAN_spc | MMAN_nl);
676 	print_word(suffix);
677 }
678 
679 static int
680 pre_ex(DECL_ARGS)
681 {
682 	int	 nchild;
683 
684 	outflags |= MMAN_br | MMAN_nl;
685 
686 	print_word("The");
687 
688 	nchild = n->nchild;
689 	for (n = n->child; n; n = n->next) {
690 		font_push('B');
691 		print_word(n->string);
692 		font_pop();
693 
694 		if (n->next == NULL)
695 			continue;
696 
697 		if (nchild > 2) {
698 			outflags &= ~MMAN_spc;
699 			print_word(",");
700 		}
701 		if (n->next->next == NULL)
702 			print_word("and");
703 	}
704 
705 	if (nchild > 1)
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 		/* FALLTHROUGH */
820 	case MDOC_Fn:
821 		/* FALLTHROUGH */
822 	case MDOC_Fo:
823 		/* FALLTHROUGH */
824 	case MDOC_In:
825 		/* FALLTHROUGH */
826 	case MDOC_Vt:
827 		outflags |= MMAN_sp;
828 		break;
829 	case MDOC_Ft:
830 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
831 			outflags |= MMAN_sp;
832 			break;
833 		}
834 		/* FALLTHROUGH */
835 	default:
836 		outflags |= MMAN_br;
837 		break;
838 	}
839 }
840 
841 static int
842 pre_an(DECL_ARGS)
843 {
844 
845 	switch (n->norm->An.auth) {
846 	case AUTH_split:
847 		outflags &= ~MMAN_An_nosplit;
848 		outflags |= MMAN_An_split;
849 		return(0);
850 	case AUTH_nosplit:
851 		outflags &= ~MMAN_An_split;
852 		outflags |= MMAN_An_nosplit;
853 		return(0);
854 	default:
855 		if (MMAN_An_split & outflags)
856 			outflags |= MMAN_br;
857 		else if (SEC_AUTHORS == n->sec &&
858 		    ! (MMAN_An_nosplit & outflags))
859 			outflags |= MMAN_An_split;
860 		return(1);
861 	}
862 }
863 
864 static int
865 pre_ap(DECL_ARGS)
866 {
867 
868 	outflags &= ~MMAN_spc;
869 	print_word("'");
870 	outflags &= ~MMAN_spc;
871 	return(0);
872 }
873 
874 static int
875 pre_aq(DECL_ARGS)
876 {
877 
878 	print_word(n->nchild == 1 &&
879 	    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
880 	outflags &= ~MMAN_spc;
881 	return(1);
882 }
883 
884 static void
885 post_aq(DECL_ARGS)
886 {
887 
888 	outflags &= ~(MMAN_spc | MMAN_nl);
889 	print_word(n->nchild == 1 &&
890 	    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
891 }
892 
893 static int
894 pre_bd(DECL_ARGS)
895 {
896 
897 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
898 
899 	if (DISP_unfilled == n->norm->Bd.type ||
900 	    DISP_literal  == n->norm->Bd.type)
901 		print_line(".nf", 0);
902 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
903 		outflags |= MMAN_sp;
904 	print_offs(n->norm->Bd.offs, 1);
905 	return(1);
906 }
907 
908 static void
909 post_bd(DECL_ARGS)
910 {
911 
912 	/* Close out this display. */
913 	print_line(".RE", MMAN_nl);
914 	if (DISP_unfilled == n->norm->Bd.type ||
915 	    DISP_literal  == n->norm->Bd.type)
916 		print_line(".fi", MMAN_nl);
917 
918 	/* Maybe we are inside an enclosing list? */
919 	if (NULL != n->parent->next)
920 		mid_it();
921 }
922 
923 static int
924 pre_bf(DECL_ARGS)
925 {
926 
927 	switch (n->type) {
928 	case ROFFT_BLOCK:
929 		return(1);
930 	case ROFFT_BODY:
931 		break;
932 	default:
933 		return(0);
934 	}
935 	switch (n->norm->Bf.font) {
936 	case FONT_Em:
937 		font_push('I');
938 		break;
939 	case FONT_Sy:
940 		font_push('B');
941 		break;
942 	default:
943 		font_push('R');
944 		break;
945 	}
946 	return(1);
947 }
948 
949 static void
950 post_bf(DECL_ARGS)
951 {
952 
953 	if (n->type == ROFFT_BODY)
954 		font_pop();
955 }
956 
957 static int
958 pre_bk(DECL_ARGS)
959 {
960 
961 	switch (n->type) {
962 	case ROFFT_BLOCK:
963 		return(1);
964 	case ROFFT_BODY:
965 		outflags |= MMAN_Bk;
966 		return(1);
967 	default:
968 		return(0);
969 	}
970 }
971 
972 static void
973 post_bk(DECL_ARGS)
974 {
975 
976 	if (n->type == ROFFT_BODY)
977 		outflags &= ~MMAN_Bk;
978 }
979 
980 static int
981 pre_bl(DECL_ARGS)
982 {
983 	size_t		 icol;
984 
985 	/*
986 	 * print_offs() will increase the -offset to account for
987 	 * a possible enclosing .It, but any enclosed .It blocks
988 	 * just nest and do not add up their indentation.
989 	 */
990 	if (n->norm->Bl.offs) {
991 		print_offs(n->norm->Bl.offs, 0);
992 		Bl_stack[Bl_stack_len++] = 0;
993 	}
994 
995 	switch (n->norm->Bl.type) {
996 	case LIST_enum:
997 		n->norm->Bl.count = 0;
998 		return(1);
999 	case LIST_column:
1000 		break;
1001 	default:
1002 		return(1);
1003 	}
1004 
1005 	if (n->nchild) {
1006 		print_line(".TS", MMAN_nl);
1007 		for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1008 			print_word("l");
1009 		print_word(".");
1010 	}
1011 	outflags |= MMAN_nl;
1012 	return(1);
1013 }
1014 
1015 static void
1016 post_bl(DECL_ARGS)
1017 {
1018 
1019 	switch (n->norm->Bl.type) {
1020 	case LIST_column:
1021 		if (n->nchild)
1022 			print_line(".TE", 0);
1023 		break;
1024 	case LIST_enum:
1025 		n->norm->Bl.count = 0;
1026 		break;
1027 	default:
1028 		break;
1029 	}
1030 
1031 	if (n->norm->Bl.offs) {
1032 		print_line(".RE", MMAN_nl);
1033 		assert(Bl_stack_len);
1034 		Bl_stack_len--;
1035 		assert(0 == Bl_stack[Bl_stack_len]);
1036 	} else {
1037 		outflags |= MMAN_PP | MMAN_nl;
1038 		outflags &= ~(MMAN_sp | MMAN_br);
1039 	}
1040 
1041 	/* Maybe we are inside an enclosing list? */
1042 	if (NULL != n->parent->next)
1043 		mid_it();
1044 
1045 }
1046 
1047 static int
1048 pre_br(DECL_ARGS)
1049 {
1050 
1051 	outflags |= MMAN_br;
1052 	return(0);
1053 }
1054 
1055 static int
1056 pre_bx(DECL_ARGS)
1057 {
1058 
1059 	n = n->child;
1060 	if (n) {
1061 		print_word(n->string);
1062 		outflags &= ~MMAN_spc;
1063 		n = n->next;
1064 	}
1065 	print_word("BSD");
1066 	if (NULL == n)
1067 		return(0);
1068 	outflags &= ~MMAN_spc;
1069 	print_word("-");
1070 	outflags &= ~MMAN_spc;
1071 	print_word(n->string);
1072 	return(0);
1073 }
1074 
1075 static int
1076 pre_dl(DECL_ARGS)
1077 {
1078 
1079 	print_offs("6n", 0);
1080 	return(1);
1081 }
1082 
1083 static void
1084 post_dl(DECL_ARGS)
1085 {
1086 
1087 	print_line(".RE", MMAN_nl);
1088 
1089 	/* Maybe we are inside an enclosing list? */
1090 	if (NULL != n->parent->next)
1091 		mid_it();
1092 }
1093 
1094 static int
1095 pre_em(DECL_ARGS)
1096 {
1097 
1098 	font_push('I');
1099 	return(1);
1100 }
1101 
1102 static int
1103 pre_en(DECL_ARGS)
1104 {
1105 
1106 	if (NULL == n->norm->Es ||
1107 	    NULL == n->norm->Es->child)
1108 		return(1);
1109 
1110 	print_word(n->norm->Es->child->string);
1111 	outflags &= ~MMAN_spc;
1112 	return(1);
1113 }
1114 
1115 static void
1116 post_en(DECL_ARGS)
1117 {
1118 
1119 	if (NULL == n->norm->Es ||
1120 	    NULL == n->norm->Es->child ||
1121 	    NULL == n->norm->Es->child->next)
1122 		return;
1123 
1124 	outflags &= ~MMAN_spc;
1125 	print_word(n->norm->Es->child->next->string);
1126 	return;
1127 }
1128 
1129 static int
1130 pre_eo(DECL_ARGS)
1131 {
1132 
1133 	if (n->end == ENDBODY_NOT &&
1134 	    n->parent->head->child == NULL &&
1135 	    n->child != NULL &&
1136 	    n->child->end != ENDBODY_NOT)
1137 		print_word("\\&");
1138 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1139 	    n->parent->head->child != NULL && (n->child != NULL ||
1140 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1141 		outflags &= ~(MMAN_spc | MMAN_nl);
1142 	return(1);
1143 }
1144 
1145 static void
1146 post_eo(DECL_ARGS)
1147 {
1148 	int	 body, tail;
1149 
1150 	if (n->end != ENDBODY_NOT) {
1151 		outflags |= MMAN_spc;
1152 		return;
1153 	}
1154 
1155 	body = n->child != NULL || n->parent->head->child != NULL;
1156 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1157 
1158 	if (body && tail)
1159 		outflags &= ~MMAN_spc;
1160 	else if ( ! (body || tail))
1161 		print_word("\\&");
1162 	else if ( ! tail)
1163 		outflags |= MMAN_spc;
1164 }
1165 
1166 static int
1167 pre_fa(DECL_ARGS)
1168 {
1169 	int	 am_Fa;
1170 
1171 	am_Fa = MDOC_Fa == n->tok;
1172 
1173 	if (am_Fa)
1174 		n = n->child;
1175 
1176 	while (NULL != n) {
1177 		font_push('I');
1178 		if (am_Fa || MDOC_SYNPRETTY & n->flags)
1179 			outflags |= MMAN_nbrword;
1180 		print_node(meta, n);
1181 		font_pop();
1182 		if (NULL != (n = n->next))
1183 			print_word(",");
1184 	}
1185 	return(0);
1186 }
1187 
1188 static void
1189 post_fa(DECL_ARGS)
1190 {
1191 
1192 	if (NULL != n->next && MDOC_Fa == n->next->tok)
1193 		print_word(",");
1194 }
1195 
1196 static int
1197 pre_fd(DECL_ARGS)
1198 {
1199 
1200 	pre_syn(n);
1201 	font_push('B');
1202 	return(1);
1203 }
1204 
1205 static void
1206 post_fd(DECL_ARGS)
1207 {
1208 
1209 	font_pop();
1210 	outflags |= MMAN_br;
1211 }
1212 
1213 static int
1214 pre_fl(DECL_ARGS)
1215 {
1216 
1217 	font_push('B');
1218 	print_word("\\-");
1219 	if (n->nchild)
1220 		outflags &= ~MMAN_spc;
1221 	return(1);
1222 }
1223 
1224 static void
1225 post_fl(DECL_ARGS)
1226 {
1227 
1228 	font_pop();
1229 	if ( ! (n->nchild ||
1230 	    n->next == NULL ||
1231 	    n->next->type == ROFFT_TEXT ||
1232 	    n->next->flags & MDOC_LINE))
1233 		outflags &= ~MMAN_spc;
1234 }
1235 
1236 static int
1237 pre_fn(DECL_ARGS)
1238 {
1239 
1240 	pre_syn(n);
1241 
1242 	n = n->child;
1243 	if (NULL == n)
1244 		return(0);
1245 
1246 	if (MDOC_SYNPRETTY & n->flags)
1247 		print_block(".HP 4n", MMAN_nl);
1248 
1249 	font_push('B');
1250 	print_node(meta, n);
1251 	font_pop();
1252 	outflags &= ~MMAN_spc;
1253 	print_word("(");
1254 	outflags &= ~MMAN_spc;
1255 
1256 	n = n->next;
1257 	if (NULL != n)
1258 		pre_fa(meta, n);
1259 	return(0);
1260 }
1261 
1262 static void
1263 post_fn(DECL_ARGS)
1264 {
1265 
1266 	print_word(")");
1267 	if (MDOC_SYNPRETTY & n->flags) {
1268 		print_word(";");
1269 		outflags |= MMAN_PP;
1270 	}
1271 }
1272 
1273 static int
1274 pre_fo(DECL_ARGS)
1275 {
1276 
1277 	switch (n->type) {
1278 	case ROFFT_BLOCK:
1279 		pre_syn(n);
1280 		break;
1281 	case ROFFT_HEAD:
1282 		if (n->child == NULL)
1283 			return(0);
1284 		if (MDOC_SYNPRETTY & n->flags)
1285 			print_block(".HP 4n", MMAN_nl);
1286 		font_push('B');
1287 		break;
1288 	case ROFFT_BODY:
1289 		outflags &= ~(MMAN_spc | MMAN_nl);
1290 		print_word("(");
1291 		outflags &= ~MMAN_spc;
1292 		break;
1293 	default:
1294 		break;
1295 	}
1296 	return(1);
1297 }
1298 
1299 static void
1300 post_fo(DECL_ARGS)
1301 {
1302 
1303 	switch (n->type) {
1304 	case ROFFT_HEAD:
1305 		if (n->child != NULL)
1306 			font_pop();
1307 		break;
1308 	case ROFFT_BODY:
1309 		post_fn(meta, n);
1310 		break;
1311 	default:
1312 		break;
1313 	}
1314 }
1315 
1316 static int
1317 pre_ft(DECL_ARGS)
1318 {
1319 
1320 	pre_syn(n);
1321 	font_push('I');
1322 	return(1);
1323 }
1324 
1325 static int
1326 pre_in(DECL_ARGS)
1327 {
1328 
1329 	if (MDOC_SYNPRETTY & n->flags) {
1330 		pre_syn(n);
1331 		font_push('B');
1332 		print_word("#include <");
1333 		outflags &= ~MMAN_spc;
1334 	} else {
1335 		print_word("<");
1336 		outflags &= ~MMAN_spc;
1337 		font_push('I');
1338 	}
1339 	return(1);
1340 }
1341 
1342 static void
1343 post_in(DECL_ARGS)
1344 {
1345 
1346 	if (MDOC_SYNPRETTY & n->flags) {
1347 		outflags &= ~MMAN_spc;
1348 		print_word(">");
1349 		font_pop();
1350 		outflags |= MMAN_br;
1351 	} else {
1352 		font_pop();
1353 		outflags &= ~MMAN_spc;
1354 		print_word(">");
1355 	}
1356 }
1357 
1358 static int
1359 pre_it(DECL_ARGS)
1360 {
1361 	const struct roff_node *bln;
1362 
1363 	switch (n->type) {
1364 	case ROFFT_HEAD:
1365 		outflags |= MMAN_PP | MMAN_nl;
1366 		bln = n->parent->parent;
1367 		if (0 == bln->norm->Bl.comp ||
1368 		    (NULL == n->parent->prev &&
1369 		     NULL == bln->parent->prev))
1370 			outflags |= MMAN_sp;
1371 		outflags &= ~MMAN_br;
1372 		switch (bln->norm->Bl.type) {
1373 		case LIST_item:
1374 			return(0);
1375 		case LIST_inset:
1376 			/* FALLTHROUGH */
1377 		case LIST_diag:
1378 			/* FALLTHROUGH */
1379 		case LIST_ohang:
1380 			if (bln->norm->Bl.type == LIST_diag)
1381 				print_line(".B \"", 0);
1382 			else
1383 				print_line(".R \"", 0);
1384 			outflags &= ~MMAN_spc;
1385 			return(1);
1386 		case LIST_bullet:
1387 			/* FALLTHROUGH */
1388 		case LIST_dash:
1389 			/* FALLTHROUGH */
1390 		case LIST_hyphen:
1391 			print_width(&bln->norm->Bl, NULL);
1392 			TPremain = 0;
1393 			outflags |= MMAN_nl;
1394 			font_push('B');
1395 			if (LIST_bullet == bln->norm->Bl.type)
1396 				print_word("\\(bu");
1397 			else
1398 				print_word("-");
1399 			font_pop();
1400 			outflags |= MMAN_nl;
1401 			return(0);
1402 		case LIST_enum:
1403 			print_width(&bln->norm->Bl, NULL);
1404 			TPremain = 0;
1405 			outflags |= MMAN_nl;
1406 			print_count(&bln->norm->Bl.count);
1407 			outflags |= MMAN_nl;
1408 			return(0);
1409 		case LIST_hang:
1410 			print_width(&bln->norm->Bl, n->child);
1411 			TPremain = 0;
1412 			outflags |= MMAN_nl;
1413 			return(1);
1414 		case LIST_tag:
1415 			print_width(&bln->norm->Bl, n->child);
1416 			putchar('\n');
1417 			outflags &= ~MMAN_spc;
1418 			return(1);
1419 		default:
1420 			return(1);
1421 		}
1422 	default:
1423 		break;
1424 	}
1425 	return(1);
1426 }
1427 
1428 /*
1429  * This function is called after closing out an indented block.
1430  * If we are inside an enclosing list, restore its indentation.
1431  */
1432 static void
1433 mid_it(void)
1434 {
1435 	char		 buf[24];
1436 
1437 	/* Nothing to do outside a list. */
1438 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1439 		return;
1440 
1441 	/* The indentation has already been set up. */
1442 	if (Bl_stack_post[Bl_stack_len - 1])
1443 		return;
1444 
1445 	/* Restore the indentation of the enclosing list. */
1446 	print_line(".RS", MMAN_Bk_susp);
1447 	(void)snprintf(buf, sizeof(buf), "%dn",
1448 	    Bl_stack[Bl_stack_len - 1]);
1449 	print_word(buf);
1450 
1451 	/* Remeber to close out this .RS block later. */
1452 	Bl_stack_post[Bl_stack_len - 1] = 1;
1453 }
1454 
1455 static void
1456 post_it(DECL_ARGS)
1457 {
1458 	const struct roff_node *bln;
1459 
1460 	bln = n->parent->parent;
1461 
1462 	switch (n->type) {
1463 	case ROFFT_HEAD:
1464 		switch (bln->norm->Bl.type) {
1465 		case LIST_diag:
1466 			outflags &= ~MMAN_spc;
1467 			print_word("\\ ");
1468 			break;
1469 		case LIST_ohang:
1470 			outflags |= MMAN_br;
1471 			break;
1472 		default:
1473 			break;
1474 		}
1475 		break;
1476 	case ROFFT_BODY:
1477 		switch (bln->norm->Bl.type) {
1478 		case LIST_bullet:
1479 			/* FALLTHROUGH */
1480 		case LIST_dash:
1481 			/* FALLTHROUGH */
1482 		case LIST_hyphen:
1483 			/* FALLTHROUGH */
1484 		case LIST_enum:
1485 			/* FALLTHROUGH */
1486 		case LIST_hang:
1487 			/* FALLTHROUGH */
1488 		case LIST_tag:
1489 			assert(Bl_stack_len);
1490 			Bl_stack[--Bl_stack_len] = 0;
1491 
1492 			/*
1493 			 * Our indentation had to be restored
1494 			 * after a child display or child list.
1495 			 * Close out that indentation block now.
1496 			 */
1497 			if (Bl_stack_post[Bl_stack_len]) {
1498 				print_line(".RE", MMAN_nl);
1499 				Bl_stack_post[Bl_stack_len] = 0;
1500 			}
1501 			break;
1502 		case LIST_column:
1503 			if (NULL != n->next) {
1504 				putchar('\t');
1505 				outflags &= ~MMAN_spc;
1506 			}
1507 			break;
1508 		default:
1509 			break;
1510 		}
1511 		break;
1512 	default:
1513 		break;
1514 	}
1515 }
1516 
1517 static void
1518 post_lb(DECL_ARGS)
1519 {
1520 
1521 	if (SEC_LIBRARY == n->sec)
1522 		outflags |= MMAN_br;
1523 }
1524 
1525 static int
1526 pre_lk(DECL_ARGS)
1527 {
1528 	const struct roff_node *link, *descr;
1529 
1530 	if (NULL == (link = n->child))
1531 		return(0);
1532 
1533 	if (NULL != (descr = link->next)) {
1534 		font_push('I');
1535 		while (NULL != descr) {
1536 			print_word(descr->string);
1537 			descr = descr->next;
1538 		}
1539 		print_word(":");
1540 		font_pop();
1541 	}
1542 
1543 	font_push('B');
1544 	print_word(link->string);
1545 	font_pop();
1546 	return(0);
1547 }
1548 
1549 static int
1550 pre_ll(DECL_ARGS)
1551 {
1552 
1553 	print_line(".ll", 0);
1554 	return(1);
1555 }
1556 
1557 static int
1558 pre_li(DECL_ARGS)
1559 {
1560 
1561 	font_push('R');
1562 	return(1);
1563 }
1564 
1565 static int
1566 pre_nm(DECL_ARGS)
1567 {
1568 	char	*name;
1569 
1570 	if (n->type == ROFFT_BLOCK) {
1571 		outflags |= MMAN_Bk;
1572 		pre_syn(n);
1573 	}
1574 	if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD)
1575 		return(1);
1576 	name = n->child ? n->child->string : meta->name;
1577 	if (NULL == name)
1578 		return(0);
1579 	if (n->type == ROFFT_HEAD) {
1580 		if (NULL == n->parent->prev)
1581 			outflags |= MMAN_sp;
1582 		print_block(".HP", 0);
1583 		printf(" %zun", strlen(name) + 1);
1584 		outflags |= MMAN_nl;
1585 	}
1586 	font_push('B');
1587 	if (NULL == n->child)
1588 		print_word(meta->name);
1589 	return(1);
1590 }
1591 
1592 static void
1593 post_nm(DECL_ARGS)
1594 {
1595 
1596 	switch (n->type) {
1597 	case ROFFT_BLOCK:
1598 		outflags &= ~MMAN_Bk;
1599 		break;
1600 	case ROFFT_HEAD:
1601 		/* FALLTHROUGH */
1602 	case ROFFT_ELEM:
1603 		if (n->child != NULL || meta->name != NULL)
1604 			font_pop();
1605 		break;
1606 	default:
1607 		break;
1608 	}
1609 }
1610 
1611 static int
1612 pre_no(DECL_ARGS)
1613 {
1614 
1615 	outflags |= MMAN_spc_force;
1616 	return(1);
1617 }
1618 
1619 static int
1620 pre_ns(DECL_ARGS)
1621 {
1622 
1623 	outflags &= ~MMAN_spc;
1624 	return(0);
1625 }
1626 
1627 static void
1628 post_pf(DECL_ARGS)
1629 {
1630 
1631 	if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
1632 		outflags &= ~MMAN_spc;
1633 }
1634 
1635 static int
1636 pre_pp(DECL_ARGS)
1637 {
1638 
1639 	if (MDOC_It != n->parent->tok)
1640 		outflags |= MMAN_PP;
1641 	outflags |= MMAN_sp | MMAN_nl;
1642 	outflags &= ~MMAN_br;
1643 	return(0);
1644 }
1645 
1646 static int
1647 pre_rs(DECL_ARGS)
1648 {
1649 
1650 	if (SEC_SEE_ALSO == n->sec) {
1651 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1652 		outflags &= ~MMAN_br;
1653 	}
1654 	return(1);
1655 }
1656 
1657 static int
1658 pre_rv(DECL_ARGS)
1659 {
1660 	int	 nchild;
1661 
1662 	outflags |= MMAN_br | MMAN_nl;
1663 
1664 	nchild = n->nchild;
1665 	if (nchild > 0) {
1666 		print_word("The");
1667 
1668 		for (n = n->child; n; n = n->next) {
1669 			font_push('B');
1670 			print_word(n->string);
1671 			font_pop();
1672 
1673 			outflags &= ~MMAN_spc;
1674 			print_word("()");
1675 
1676 			if (n->next == NULL)
1677 				continue;
1678 
1679 			if (nchild > 2) {
1680 				outflags &= ~MMAN_spc;
1681 				print_word(",");
1682 			}
1683 			if (n->next->next == NULL)
1684 				print_word("and");
1685 		}
1686 
1687 		if (nchild > 1)
1688 			print_word("functions return");
1689 		else
1690 			print_word("function returns");
1691 
1692 		print_word("the value\\~0 if successful;");
1693 	} else
1694 		print_word("Upon successful completion, "
1695 		    "the value\\~0 is returned;");
1696 
1697 	print_word("otherwise the value\\~\\-1 is returned"
1698 	    " and the global variable");
1699 
1700 	font_push('I');
1701 	print_word("errno");
1702 	font_pop();
1703 
1704 	print_word("is set to indicate the error.");
1705 	outflags |= MMAN_nl;
1706 	return(0);
1707 }
1708 
1709 static int
1710 pre_skip(DECL_ARGS)
1711 {
1712 
1713 	return(0);
1714 }
1715 
1716 static int
1717 pre_sm(DECL_ARGS)
1718 {
1719 
1720 	if (NULL == n->child)
1721 		outflags ^= MMAN_Sm;
1722 	else if (0 == strcmp("on", n->child->string))
1723 		outflags |= MMAN_Sm;
1724 	else
1725 		outflags &= ~MMAN_Sm;
1726 
1727 	if (MMAN_Sm & outflags)
1728 		outflags |= MMAN_spc;
1729 
1730 	return(0);
1731 }
1732 
1733 static int
1734 pre_sp(DECL_ARGS)
1735 {
1736 
1737 	if (MMAN_PP & outflags) {
1738 		outflags &= ~MMAN_PP;
1739 		print_line(".PP", 0);
1740 	} else
1741 		print_line(".sp", 0);
1742 	return(1);
1743 }
1744 
1745 static void
1746 post_sp(DECL_ARGS)
1747 {
1748 
1749 	outflags |= MMAN_nl;
1750 }
1751 
1752 static int
1753 pre_sy(DECL_ARGS)
1754 {
1755 
1756 	font_push('B');
1757 	return(1);
1758 }
1759 
1760 static int
1761 pre_vt(DECL_ARGS)
1762 {
1763 
1764 	if (MDOC_SYNPRETTY & n->flags) {
1765 		switch (n->type) {
1766 		case ROFFT_BLOCK:
1767 			pre_syn(n);
1768 			return(1);
1769 		case ROFFT_BODY:
1770 			break;
1771 		default:
1772 			return(0);
1773 		}
1774 	}
1775 	font_push('I');
1776 	return(1);
1777 }
1778 
1779 static void
1780 post_vt(DECL_ARGS)
1781 {
1782 
1783 	if (n->flags & MDOC_SYNPRETTY && n->type != ROFFT_BODY)
1784 		return;
1785 	font_pop();
1786 }
1787 
1788 static int
1789 pre_xr(DECL_ARGS)
1790 {
1791 
1792 	n = n->child;
1793 	if (NULL == n)
1794 		return(0);
1795 	print_node(meta, n);
1796 	n = n->next;
1797 	if (NULL == n)
1798 		return(0);
1799 	outflags &= ~MMAN_spc;
1800 	print_word("(");
1801 	print_node(meta, n);
1802 	print_word(")");
1803 	return(0);
1804 }
1805 
1806 static int
1807 pre_ux(DECL_ARGS)
1808 {
1809 
1810 	print_word(manacts[n->tok].prefix);
1811 	if (NULL == n->child)
1812 		return(0);
1813 	outflags &= ~MMAN_spc;
1814 	print_word("\\ ");
1815 	outflags &= ~MMAN_spc;
1816 	return(1);
1817 }
1818