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