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