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