xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision f6b75673f6c960a9743bfd16c1e52dd100265c68)
1 /*	$OpenBSD: mdoc_man.c,v 1.126 2018/08/23 19:32:03 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	mdoc_man_act {
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_man_acts[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 mdoc_man_act mdoc_man_acts[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 mdoc_man_act *mdoc_man_act(enum roff_tok);
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 const struct mdoc_man_act *
292 mdoc_man_act(enum roff_tok tok)
293 {
294 	assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
295 	return mdoc_man_acts + (tok - MDOC_Dd);
296 }
297 
298 static int
299 man_strlen(const char *cp)
300 {
301 	size_t	 rsz;
302 	int	 skip, sz;
303 
304 	sz = 0;
305 	skip = 0;
306 	for (;;) {
307 		rsz = strcspn(cp, "\\");
308 		if (rsz) {
309 			cp += rsz;
310 			if (skip) {
311 				skip = 0;
312 				rsz--;
313 			}
314 			sz += rsz;
315 		}
316 		if ('\0' == *cp)
317 			break;
318 		cp++;
319 		switch (mandoc_escape(&cp, NULL, NULL)) {
320 		case ESCAPE_ERROR:
321 			return sz;
322 		case ESCAPE_UNICODE:
323 		case ESCAPE_NUMBERED:
324 		case ESCAPE_SPECIAL:
325 		case ESCAPE_OVERSTRIKE:
326 			if (skip)
327 				skip = 0;
328 			else
329 				sz++;
330 			break;
331 		case ESCAPE_SKIPCHAR:
332 			skip = 1;
333 			break;
334 		default:
335 			break;
336 		}
337 	}
338 	return sz;
339 }
340 
341 static void
342 font_push(char newfont)
343 {
344 
345 	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
346 		fontqueue.size += 8;
347 		fontqueue.head = mandoc_realloc(fontqueue.head,
348 		    fontqueue.size);
349 	}
350 	*fontqueue.tail = newfont;
351 	print_word("");
352 	printf("\\f");
353 	putchar(newfont);
354 	outflags &= ~MMAN_spc;
355 }
356 
357 static void
358 font_pop(void)
359 {
360 
361 	if (fontqueue.tail > fontqueue.head)
362 		fontqueue.tail--;
363 	outflags &= ~MMAN_spc;
364 	print_word("");
365 	printf("\\f");
366 	putchar(*fontqueue.tail);
367 }
368 
369 static void
370 print_word(const char *s)
371 {
372 
373 	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
374 		/*
375 		 * If we need a newline, print it now and start afresh.
376 		 */
377 		if (MMAN_PP & outflags) {
378 			if (MMAN_sp & outflags) {
379 				if (MMAN_PD & outflags) {
380 					printf("\n.PD");
381 					outflags &= ~MMAN_PD;
382 				}
383 			} else if ( ! (MMAN_PD & outflags)) {
384 				printf("\n.PD 0");
385 				outflags |= MMAN_PD;
386 			}
387 			printf("\n.PP\n");
388 		} else if (MMAN_sp & outflags)
389 			printf("\n.sp\n");
390 		else if (MMAN_br & outflags)
391 			printf("\n.br\n");
392 		else if (MMAN_nl & outflags)
393 			putchar('\n');
394 		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
395 		if (1 == TPremain)
396 			printf(".br\n");
397 		TPremain = 0;
398 	} else if (MMAN_spc & outflags) {
399 		/*
400 		 * If we need a space, only print it if
401 		 * (1) it is forced by `No' or
402 		 * (2) what follows is not terminating punctuation or
403 		 * (3) what follows is longer than one character.
404 		 */
405 		if (MMAN_spc_force & outflags || '\0' == s[0] ||
406 		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
407 			if (MMAN_Bk & outflags &&
408 			    ! (MMAN_Bk_susp & outflags))
409 				putchar('\\');
410 			putchar(' ');
411 			if (TPremain)
412 				TPremain--;
413 		}
414 	}
415 
416 	/*
417 	 * Reassign needing space if we're not following opening
418 	 * punctuation.
419 	 */
420 	if (MMAN_Sm & outflags && ('\0' == s[0] ||
421 	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
422 		outflags |= MMAN_spc;
423 	else
424 		outflags &= ~MMAN_spc;
425 	outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
426 
427 	for ( ; *s; s++) {
428 		switch (*s) {
429 		case ASCII_NBRSP:
430 			printf("\\ ");
431 			break;
432 		case ASCII_HYPH:
433 			putchar('-');
434 			break;
435 		case ASCII_BREAK:
436 			printf("\\:");
437 			break;
438 		case ' ':
439 			if (MMAN_nbrword & outflags) {
440 				printf("\\ ");
441 				break;
442 			}
443 			/* FALLTHROUGH */
444 		default:
445 			putchar((unsigned char)*s);
446 			break;
447 		}
448 		if (TPremain)
449 			TPremain--;
450 	}
451 	outflags &= ~MMAN_nbrword;
452 }
453 
454 static void
455 print_line(const char *s, int newflags)
456 {
457 
458 	outflags |= MMAN_nl;
459 	print_word(s);
460 	outflags |= newflags;
461 }
462 
463 static void
464 print_block(const char *s, int newflags)
465 {
466 
467 	outflags &= ~MMAN_PP;
468 	if (MMAN_sp & outflags) {
469 		outflags &= ~(MMAN_sp | MMAN_br);
470 		if (MMAN_PD & outflags) {
471 			print_line(".PD", 0);
472 			outflags &= ~MMAN_PD;
473 		}
474 	} else if (! (MMAN_PD & outflags))
475 		print_line(".PD 0", MMAN_PD);
476 	outflags |= MMAN_nl;
477 	print_word(s);
478 	outflags |= MMAN_Bk_susp | newflags;
479 }
480 
481 static void
482 print_offs(const char *v, int keywords)
483 {
484 	char		  buf[24];
485 	struct roffsu	  su;
486 	const char	 *end;
487 	int		  sz;
488 
489 	print_line(".RS", MMAN_Bk_susp);
490 
491 	/* Convert v into a number (of characters). */
492 	if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
493 		sz = 0;
494 	else if (keywords && !strcmp(v, "indent"))
495 		sz = 6;
496 	else if (keywords && !strcmp(v, "indent-two"))
497 		sz = 12;
498 	else {
499 		end = a2roffsu(v, &su, SCALE_EN);
500 		if (end == NULL || *end != '\0')
501 			sz = man_strlen(v);
502 		else if (SCALE_EN == su.unit)
503 			sz = su.scale;
504 		else {
505 			/*
506 			 * XXX
507 			 * If we are inside an enclosing list,
508 			 * there is no easy way to add the two
509 			 * indentations because they are provided
510 			 * in terms of different units.
511 			 */
512 			print_word(v);
513 			outflags |= MMAN_nl;
514 			return;
515 		}
516 	}
517 
518 	/*
519 	 * We are inside an enclosing list.
520 	 * Add the two indentations.
521 	 */
522 	if (Bl_stack_len)
523 		sz += Bl_stack[Bl_stack_len - 1];
524 
525 	(void)snprintf(buf, sizeof(buf), "%dn", sz);
526 	print_word(buf);
527 	outflags |= MMAN_nl;
528 }
529 
530 /*
531  * Set up the indentation for a list item; used from pre_it().
532  */
533 static void
534 print_width(const struct mdoc_bl *bl, const struct roff_node *child)
535 {
536 	char		  buf[24];
537 	struct roffsu	  su;
538 	const char	 *end;
539 	int		  numeric, remain, sz, chsz;
540 
541 	numeric = 1;
542 	remain = 0;
543 
544 	/* Convert the width into a number (of characters). */
545 	if (bl->width == NULL)
546 		sz = (bl->type == LIST_hang) ? 6 : 0;
547 	else {
548 		end = a2roffsu(bl->width, &su, SCALE_MAX);
549 		if (end == NULL || *end != '\0')
550 			sz = man_strlen(bl->width);
551 		else if (SCALE_EN == su.unit)
552 			sz = su.scale;
553 		else {
554 			sz = 0;
555 			numeric = 0;
556 		}
557 	}
558 
559 	/* XXX Rough estimation, might have multiple parts. */
560 	if (bl->type == LIST_enum)
561 		chsz = (bl->count > 8) + 1;
562 	else if (child != NULL && child->type == ROFFT_TEXT)
563 		chsz = man_strlen(child->string);
564 	else
565 		chsz = 0;
566 
567 	/* Maybe we are inside an enclosing list? */
568 	mid_it();
569 
570 	/*
571 	 * Save our own indentation,
572 	 * such that child lists can use it.
573 	 */
574 	Bl_stack[Bl_stack_len++] = sz + 2;
575 
576 	/* Set up the current list. */
577 	if (chsz > sz && bl->type != LIST_tag)
578 		print_block(".HP", 0);
579 	else {
580 		print_block(".TP", 0);
581 		remain = sz + 2;
582 	}
583 	if (numeric) {
584 		(void)snprintf(buf, sizeof(buf), "%dn", sz + 2);
585 		print_word(buf);
586 	} else
587 		print_word(bl->width);
588 	TPremain = remain;
589 }
590 
591 static void
592 print_count(int *count)
593 {
594 	char		  buf[24];
595 
596 	(void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
597 	print_word(buf);
598 }
599 
600 void
601 man_mdoc(void *arg, const struct roff_man *mdoc)
602 {
603 	struct roff_node *n;
604 
605 	printf(".\\\" Automatically generated from an mdoc input file."
606 	    "  Do not edit.\n");
607 	for (n = mdoc->first->child; n != NULL; n = n->next) {
608 		if (n->type != ROFFT_COMMENT)
609 			break;
610 		printf(".\\\"%s\n", n->string);
611 	}
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 != 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 mdoc_man_act	*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_man_acts[n->tok])(meta, n);
676 		return;
677 	} else {
678 		/*
679 		 * Conditionally run the pre-node action handler for a
680 		 * node.
681 		 */
682 		act = mdoc_man_act(n->tok);
683 		cond = act->cond == NULL || (*act->cond)(meta, n);
684 		if (cond && act->pre != NULL &&
685 		    (n->end == ENDBODY_NOT || n->child != NULL))
686 			do_sub = (*act->pre)(meta, n);
687 	}
688 
689 	/*
690 	 * Conditionally run all child nodes.
691 	 * Note that this iterates over children instead of using
692 	 * recursion.  This prevents unnecessary depth in the stack.
693 	 */
694 	if (do_sub)
695 		for (sub = n->child; sub; sub = sub->next)
696 			print_node(meta, sub);
697 
698 	/*
699 	 * Lastly, conditionally run the post-node handler.
700 	 */
701 	if (NODE_ENDED & n->flags)
702 		return;
703 
704 	if (cond && act->post)
705 		(*act->post)(meta, n);
706 
707 	if (ENDBODY_NOT != n->end)
708 		n->body->flags |= NODE_ENDED;
709 }
710 
711 static int
712 cond_head(DECL_ARGS)
713 {
714 
715 	return n->type == ROFFT_HEAD;
716 }
717 
718 static int
719 cond_body(DECL_ARGS)
720 {
721 
722 	return n->type == ROFFT_BODY;
723 }
724 
725 static int
726 pre_enc(DECL_ARGS)
727 {
728 	const char	*prefix;
729 
730 	prefix = mdoc_man_act(n->tok)->prefix;
731 	if (NULL == prefix)
732 		return 1;
733 	print_word(prefix);
734 	outflags &= ~MMAN_spc;
735 	return 1;
736 }
737 
738 static void
739 post_enc(DECL_ARGS)
740 {
741 	const char *suffix;
742 
743 	suffix = mdoc_man_act(n->tok)->suffix;
744 	if (NULL == suffix)
745 		return;
746 	outflags &= ~(MMAN_spc | MMAN_nl);
747 	print_word(suffix);
748 }
749 
750 static int
751 pre_ex(DECL_ARGS)
752 {
753 	outflags |= MMAN_br | MMAN_nl;
754 	return 1;
755 }
756 
757 static void
758 post_font(DECL_ARGS)
759 {
760 
761 	font_pop();
762 }
763 
764 static void
765 post_percent(DECL_ARGS)
766 {
767 
768 	if (mdoc_man_act(n->tok)->pre == pre_em)
769 		font_pop();
770 	if (n->next) {
771 		print_word(",");
772 		if (n->prev &&	n->prev->tok == n->tok &&
773 				n->next->tok == n->tok)
774 			print_word("and");
775 	} else {
776 		print_word(".");
777 		outflags |= MMAN_nl;
778 	}
779 }
780 
781 static int
782 pre__t(DECL_ARGS)
783 {
784 
785 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) {
786 		print_word("\\(lq");
787 		outflags &= ~MMAN_spc;
788 	} else
789 		font_push('I');
790 	return 1;
791 }
792 
793 static void
794 post__t(DECL_ARGS)
795 {
796 
797 	if (n->parent->tok  == MDOC_Rs && n->parent->norm->Rs.quote_T) {
798 		outflags &= ~MMAN_spc;
799 		print_word("\\(rq");
800 	} else
801 		font_pop();
802 	post_percent(meta, n);
803 }
804 
805 /*
806  * Print before a section header.
807  */
808 static int
809 pre_sect(DECL_ARGS)
810 {
811 
812 	if (n->type == ROFFT_HEAD) {
813 		outflags |= MMAN_sp;
814 		print_block(mdoc_man_act(n->tok)->prefix, 0);
815 		print_word("");
816 		putchar('\"');
817 		outflags &= ~MMAN_spc;
818 	}
819 	return 1;
820 }
821 
822 /*
823  * Print subsequent a section header.
824  */
825 static void
826 post_sect(DECL_ARGS)
827 {
828 
829 	if (n->type != ROFFT_HEAD)
830 		return;
831 	outflags &= ~MMAN_spc;
832 	print_word("");
833 	putchar('\"');
834 	outflags |= MMAN_nl;
835 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
836 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
837 }
838 
839 /* See mdoc_term.c, synopsis_pre() for comments. */
840 static void
841 pre_syn(const struct roff_node *n)
842 {
843 
844 	if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
845 		return;
846 
847 	if (n->prev->tok == n->tok &&
848 	    MDOC_Ft != n->tok &&
849 	    MDOC_Fo != n->tok &&
850 	    MDOC_Fn != n->tok) {
851 		outflags |= MMAN_br;
852 		return;
853 	}
854 
855 	switch (n->prev->tok) {
856 	case MDOC_Fd:
857 	case MDOC_Fn:
858 	case MDOC_Fo:
859 	case MDOC_In:
860 	case MDOC_Vt:
861 		outflags |= MMAN_sp;
862 		break;
863 	case MDOC_Ft:
864 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
865 			outflags |= MMAN_sp;
866 			break;
867 		}
868 		/* FALLTHROUGH */
869 	default:
870 		outflags |= MMAN_br;
871 		break;
872 	}
873 }
874 
875 static int
876 pre_an(DECL_ARGS)
877 {
878 
879 	switch (n->norm->An.auth) {
880 	case AUTH_split:
881 		outflags &= ~MMAN_An_nosplit;
882 		outflags |= MMAN_An_split;
883 		return 0;
884 	case AUTH_nosplit:
885 		outflags &= ~MMAN_An_split;
886 		outflags |= MMAN_An_nosplit;
887 		return 0;
888 	default:
889 		if (MMAN_An_split & outflags)
890 			outflags |= MMAN_br;
891 		else if (SEC_AUTHORS == n->sec &&
892 		    ! (MMAN_An_nosplit & outflags))
893 			outflags |= MMAN_An_split;
894 		return 1;
895 	}
896 }
897 
898 static int
899 pre_ap(DECL_ARGS)
900 {
901 
902 	outflags &= ~MMAN_spc;
903 	print_word("'");
904 	outflags &= ~MMAN_spc;
905 	return 0;
906 }
907 
908 static int
909 pre_aq(DECL_ARGS)
910 {
911 
912 	print_word(n->child != NULL && n->child->next == NULL &&
913 	    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
914 	outflags &= ~MMAN_spc;
915 	return 1;
916 }
917 
918 static void
919 post_aq(DECL_ARGS)
920 {
921 
922 	outflags &= ~(MMAN_spc | MMAN_nl);
923 	print_word(n->child != NULL && n->child->next == NULL &&
924 	    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
925 }
926 
927 static int
928 pre_bd(DECL_ARGS)
929 {
930 
931 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
932 
933 	if (DISP_unfilled == n->norm->Bd.type ||
934 	    DISP_literal  == n->norm->Bd.type)
935 		print_line(".nf", 0);
936 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
937 		outflags |= MMAN_sp;
938 	print_offs(n->norm->Bd.offs, 1);
939 	return 1;
940 }
941 
942 static void
943 post_bd(DECL_ARGS)
944 {
945 
946 	/* Close out this display. */
947 	print_line(".RE", MMAN_nl);
948 	if (DISP_unfilled == n->norm->Bd.type ||
949 	    DISP_literal  == n->norm->Bd.type)
950 		print_line(".fi", MMAN_nl);
951 
952 	/* Maybe we are inside an enclosing list? */
953 	if (NULL != n->parent->next)
954 		mid_it();
955 }
956 
957 static int
958 pre_bf(DECL_ARGS)
959 {
960 
961 	switch (n->type) {
962 	case ROFFT_BLOCK:
963 		return 1;
964 	case ROFFT_BODY:
965 		break;
966 	default:
967 		return 0;
968 	}
969 	switch (n->norm->Bf.font) {
970 	case FONT_Em:
971 		font_push('I');
972 		break;
973 	case FONT_Sy:
974 		font_push('B');
975 		break;
976 	default:
977 		font_push('R');
978 		break;
979 	}
980 	return 1;
981 }
982 
983 static void
984 post_bf(DECL_ARGS)
985 {
986 
987 	if (n->type == ROFFT_BODY)
988 		font_pop();
989 }
990 
991 static int
992 pre_bk(DECL_ARGS)
993 {
994 	switch (n->type) {
995 	case ROFFT_BLOCK:
996 		return 1;
997 	case ROFFT_BODY:
998 	case ROFFT_ELEM:
999 		outflags |= MMAN_Bk;
1000 		return 1;
1001 	default:
1002 		return 0;
1003 	}
1004 }
1005 
1006 static void
1007 post_bk(DECL_ARGS)
1008 {
1009 	switch (n->type) {
1010 	case ROFFT_ELEM:
1011 		while ((n = n->parent) != NULL)
1012 			 if (n->tok == MDOC_Bk)
1013 				return;
1014 		/* FALLTHROUGH */
1015 	case ROFFT_BODY:
1016 		outflags &= ~MMAN_Bk;
1017 		break;
1018 	default:
1019 		break;
1020 	}
1021 }
1022 
1023 static int
1024 pre_bl(DECL_ARGS)
1025 {
1026 	size_t		 icol;
1027 
1028 	/*
1029 	 * print_offs() will increase the -offset to account for
1030 	 * a possible enclosing .It, but any enclosed .It blocks
1031 	 * just nest and do not add up their indentation.
1032 	 */
1033 	if (n->norm->Bl.offs) {
1034 		print_offs(n->norm->Bl.offs, 0);
1035 		Bl_stack[Bl_stack_len++] = 0;
1036 	}
1037 
1038 	switch (n->norm->Bl.type) {
1039 	case LIST_enum:
1040 		n->norm->Bl.count = 0;
1041 		return 1;
1042 	case LIST_column:
1043 		break;
1044 	default:
1045 		return 1;
1046 	}
1047 
1048 	if (n->child != NULL) {
1049 		print_line(".TS", MMAN_nl);
1050 		for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1051 			print_word("l");
1052 		print_word(".");
1053 	}
1054 	outflags |= MMAN_nl;
1055 	return 1;
1056 }
1057 
1058 static void
1059 post_bl(DECL_ARGS)
1060 {
1061 
1062 	switch (n->norm->Bl.type) {
1063 	case LIST_column:
1064 		if (n->child != NULL)
1065 			print_line(".TE", 0);
1066 		break;
1067 	case LIST_enum:
1068 		n->norm->Bl.count = 0;
1069 		break;
1070 	default:
1071 		break;
1072 	}
1073 
1074 	if (n->norm->Bl.offs) {
1075 		print_line(".RE", MMAN_nl);
1076 		assert(Bl_stack_len);
1077 		Bl_stack_len--;
1078 		assert(0 == Bl_stack[Bl_stack_len]);
1079 	} else {
1080 		outflags |= MMAN_PP | MMAN_nl;
1081 		outflags &= ~(MMAN_sp | MMAN_br);
1082 	}
1083 
1084 	/* Maybe we are inside an enclosing list? */
1085 	if (NULL != n->parent->next)
1086 		mid_it();
1087 
1088 }
1089 
1090 static void
1091 pre_br(DECL_ARGS)
1092 {
1093 	outflags |= MMAN_br;
1094 }
1095 
1096 static int
1097 pre_dl(DECL_ARGS)
1098 {
1099 
1100 	print_offs("6n", 0);
1101 	return 1;
1102 }
1103 
1104 static void
1105 post_dl(DECL_ARGS)
1106 {
1107 
1108 	print_line(".RE", MMAN_nl);
1109 
1110 	/* Maybe we are inside an enclosing list? */
1111 	if (NULL != n->parent->next)
1112 		mid_it();
1113 }
1114 
1115 static int
1116 pre_em(DECL_ARGS)
1117 {
1118 
1119 	font_push('I');
1120 	return 1;
1121 }
1122 
1123 static int
1124 pre_en(DECL_ARGS)
1125 {
1126 
1127 	if (NULL == n->norm->Es ||
1128 	    NULL == n->norm->Es->child)
1129 		return 1;
1130 
1131 	print_word(n->norm->Es->child->string);
1132 	outflags &= ~MMAN_spc;
1133 	return 1;
1134 }
1135 
1136 static void
1137 post_en(DECL_ARGS)
1138 {
1139 
1140 	if (NULL == n->norm->Es ||
1141 	    NULL == n->norm->Es->child ||
1142 	    NULL == n->norm->Es->child->next)
1143 		return;
1144 
1145 	outflags &= ~MMAN_spc;
1146 	print_word(n->norm->Es->child->next->string);
1147 	return;
1148 }
1149 
1150 static int
1151 pre_eo(DECL_ARGS)
1152 {
1153 
1154 	if (n->end == ENDBODY_NOT &&
1155 	    n->parent->head->child == NULL &&
1156 	    n->child != NULL &&
1157 	    n->child->end != ENDBODY_NOT)
1158 		print_word("\\&");
1159 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1160 	    n->parent->head->child != NULL && (n->child != NULL ||
1161 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1162 		outflags &= ~(MMAN_spc | MMAN_nl);
1163 	return 1;
1164 }
1165 
1166 static void
1167 post_eo(DECL_ARGS)
1168 {
1169 	int	 body, tail;
1170 
1171 	if (n->end != ENDBODY_NOT) {
1172 		outflags |= MMAN_spc;
1173 		return;
1174 	}
1175 
1176 	body = n->child != NULL || n->parent->head->child != NULL;
1177 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1178 
1179 	if (body && tail)
1180 		outflags &= ~MMAN_spc;
1181 	else if ( ! (body || tail))
1182 		print_word("\\&");
1183 	else if ( ! tail)
1184 		outflags |= MMAN_spc;
1185 }
1186 
1187 static int
1188 pre_fa(DECL_ARGS)
1189 {
1190 	int	 am_Fa;
1191 
1192 	am_Fa = MDOC_Fa == n->tok;
1193 
1194 	if (am_Fa)
1195 		n = n->child;
1196 
1197 	while (NULL != n) {
1198 		font_push('I');
1199 		if (am_Fa || NODE_SYNPRETTY & n->flags)
1200 			outflags |= MMAN_nbrword;
1201 		print_node(meta, n);
1202 		font_pop();
1203 		if (NULL != (n = n->next))
1204 			print_word(",");
1205 	}
1206 	return 0;
1207 }
1208 
1209 static void
1210 post_fa(DECL_ARGS)
1211 {
1212 
1213 	if (NULL != n->next && MDOC_Fa == n->next->tok)
1214 		print_word(",");
1215 }
1216 
1217 static int
1218 pre_fd(DECL_ARGS)
1219 {
1220 
1221 	pre_syn(n);
1222 	font_push('B');
1223 	return 1;
1224 }
1225 
1226 static void
1227 post_fd(DECL_ARGS)
1228 {
1229 
1230 	font_pop();
1231 	outflags |= MMAN_br;
1232 }
1233 
1234 static int
1235 pre_fl(DECL_ARGS)
1236 {
1237 
1238 	font_push('B');
1239 	print_word("\\-");
1240 	if (n->child != NULL)
1241 		outflags &= ~MMAN_spc;
1242 	return 1;
1243 }
1244 
1245 static void
1246 post_fl(DECL_ARGS)
1247 {
1248 
1249 	font_pop();
1250 	if (!(n->child != NULL ||
1251 	    n->next == NULL ||
1252 	    n->next->type == ROFFT_TEXT ||
1253 	    n->next->flags & NODE_LINE))
1254 		outflags &= ~MMAN_spc;
1255 }
1256 
1257 static int
1258 pre_fn(DECL_ARGS)
1259 {
1260 
1261 	pre_syn(n);
1262 
1263 	n = n->child;
1264 	if (NULL == n)
1265 		return 0;
1266 
1267 	if (NODE_SYNPRETTY & n->flags)
1268 		print_block(".HP 4n", MMAN_nl);
1269 
1270 	font_push('B');
1271 	print_node(meta, n);
1272 	font_pop();
1273 	outflags &= ~MMAN_spc;
1274 	print_word("(");
1275 	outflags &= ~MMAN_spc;
1276 
1277 	n = n->next;
1278 	if (NULL != n)
1279 		pre_fa(meta, n);
1280 	return 0;
1281 }
1282 
1283 static void
1284 post_fn(DECL_ARGS)
1285 {
1286 
1287 	print_word(")");
1288 	if (NODE_SYNPRETTY & n->flags) {
1289 		print_word(";");
1290 		outflags |= MMAN_PP;
1291 	}
1292 }
1293 
1294 static int
1295 pre_fo(DECL_ARGS)
1296 {
1297 
1298 	switch (n->type) {
1299 	case ROFFT_BLOCK:
1300 		pre_syn(n);
1301 		break;
1302 	case ROFFT_HEAD:
1303 		if (n->child == NULL)
1304 			return 0;
1305 		if (NODE_SYNPRETTY & n->flags)
1306 			print_block(".HP 4n", MMAN_nl);
1307 		font_push('B');
1308 		break;
1309 	case ROFFT_BODY:
1310 		outflags &= ~(MMAN_spc | MMAN_nl);
1311 		print_word("(");
1312 		outflags &= ~MMAN_spc;
1313 		break;
1314 	default:
1315 		break;
1316 	}
1317 	return 1;
1318 }
1319 
1320 static void
1321 post_fo(DECL_ARGS)
1322 {
1323 
1324 	switch (n->type) {
1325 	case ROFFT_HEAD:
1326 		if (n->child != NULL)
1327 			font_pop();
1328 		break;
1329 	case ROFFT_BODY:
1330 		post_fn(meta, n);
1331 		break;
1332 	default:
1333 		break;
1334 	}
1335 }
1336 
1337 static int
1338 pre_Ft(DECL_ARGS)
1339 {
1340 
1341 	pre_syn(n);
1342 	font_push('I');
1343 	return 1;
1344 }
1345 
1346 static void
1347 pre_ft(DECL_ARGS)
1348 {
1349 	print_line(".ft", 0);
1350 	print_word(n->child->string);
1351 	outflags |= MMAN_nl;
1352 }
1353 
1354 static int
1355 pre_in(DECL_ARGS)
1356 {
1357 
1358 	if (NODE_SYNPRETTY & n->flags) {
1359 		pre_syn(n);
1360 		font_push('B');
1361 		print_word("#include <");
1362 		outflags &= ~MMAN_spc;
1363 	} else {
1364 		print_word("<");
1365 		outflags &= ~MMAN_spc;
1366 		font_push('I');
1367 	}
1368 	return 1;
1369 }
1370 
1371 static void
1372 post_in(DECL_ARGS)
1373 {
1374 
1375 	if (NODE_SYNPRETTY & n->flags) {
1376 		outflags &= ~MMAN_spc;
1377 		print_word(">");
1378 		font_pop();
1379 		outflags |= MMAN_br;
1380 	} else {
1381 		font_pop();
1382 		outflags &= ~MMAN_spc;
1383 		print_word(">");
1384 	}
1385 }
1386 
1387 static int
1388 pre_it(DECL_ARGS)
1389 {
1390 	const struct roff_node *bln;
1391 
1392 	switch (n->type) {
1393 	case ROFFT_HEAD:
1394 		outflags |= MMAN_PP | MMAN_nl;
1395 		bln = n->parent->parent;
1396 		if (0 == bln->norm->Bl.comp ||
1397 		    (NULL == n->parent->prev &&
1398 		     NULL == bln->parent->prev))
1399 			outflags |= MMAN_sp;
1400 		outflags &= ~MMAN_br;
1401 		switch (bln->norm->Bl.type) {
1402 		case LIST_item:
1403 			return 0;
1404 		case LIST_inset:
1405 		case LIST_diag:
1406 		case LIST_ohang:
1407 			if (bln->norm->Bl.type == LIST_diag)
1408 				print_line(".B \"", 0);
1409 			else
1410 				print_line(".BR \\& \"", 0);
1411 			outflags &= ~MMAN_spc;
1412 			return 1;
1413 		case LIST_bullet:
1414 		case LIST_dash:
1415 		case LIST_hyphen:
1416 			print_width(&bln->norm->Bl, NULL);
1417 			TPremain = 0;
1418 			outflags |= MMAN_nl;
1419 			font_push('B');
1420 			if (LIST_bullet == bln->norm->Bl.type)
1421 				print_word("\\(bu");
1422 			else
1423 				print_word("-");
1424 			font_pop();
1425 			outflags |= MMAN_nl;
1426 			return 0;
1427 		case LIST_enum:
1428 			print_width(&bln->norm->Bl, NULL);
1429 			TPremain = 0;
1430 			outflags |= MMAN_nl;
1431 			print_count(&bln->norm->Bl.count);
1432 			outflags |= MMAN_nl;
1433 			return 0;
1434 		case LIST_hang:
1435 			print_width(&bln->norm->Bl, n->child);
1436 			TPremain = 0;
1437 			outflags |= MMAN_nl;
1438 			return 1;
1439 		case LIST_tag:
1440 			print_width(&bln->norm->Bl, n->child);
1441 			putchar('\n');
1442 			outflags &= ~MMAN_spc;
1443 			return 1;
1444 		default:
1445 			return 1;
1446 		}
1447 	default:
1448 		break;
1449 	}
1450 	return 1;
1451 }
1452 
1453 /*
1454  * This function is called after closing out an indented block.
1455  * If we are inside an enclosing list, restore its indentation.
1456  */
1457 static void
1458 mid_it(void)
1459 {
1460 	char		 buf[24];
1461 
1462 	/* Nothing to do outside a list. */
1463 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1464 		return;
1465 
1466 	/* The indentation has already been set up. */
1467 	if (Bl_stack_post[Bl_stack_len - 1])
1468 		return;
1469 
1470 	/* Restore the indentation of the enclosing list. */
1471 	print_line(".RS", MMAN_Bk_susp);
1472 	(void)snprintf(buf, sizeof(buf), "%dn",
1473 	    Bl_stack[Bl_stack_len - 1]);
1474 	print_word(buf);
1475 
1476 	/* Remeber to close out this .RS block later. */
1477 	Bl_stack_post[Bl_stack_len - 1] = 1;
1478 }
1479 
1480 static void
1481 post_it(DECL_ARGS)
1482 {
1483 	const struct roff_node *bln;
1484 
1485 	bln = n->parent->parent;
1486 
1487 	switch (n->type) {
1488 	case ROFFT_HEAD:
1489 		switch (bln->norm->Bl.type) {
1490 		case LIST_diag:
1491 			outflags &= ~MMAN_spc;
1492 			print_word("\\ ");
1493 			break;
1494 		case LIST_ohang:
1495 			outflags |= MMAN_br;
1496 			break;
1497 		default:
1498 			break;
1499 		}
1500 		break;
1501 	case ROFFT_BODY:
1502 		switch (bln->norm->Bl.type) {
1503 		case LIST_bullet:
1504 		case LIST_dash:
1505 		case LIST_hyphen:
1506 		case LIST_enum:
1507 		case LIST_hang:
1508 		case LIST_tag:
1509 			assert(Bl_stack_len);
1510 			Bl_stack[--Bl_stack_len] = 0;
1511 
1512 			/*
1513 			 * Our indentation had to be restored
1514 			 * after a child display or child list.
1515 			 * Close out that indentation block now.
1516 			 */
1517 			if (Bl_stack_post[Bl_stack_len]) {
1518 				print_line(".RE", MMAN_nl);
1519 				Bl_stack_post[Bl_stack_len] = 0;
1520 			}
1521 			break;
1522 		case LIST_column:
1523 			if (NULL != n->next) {
1524 				putchar('\t');
1525 				outflags &= ~MMAN_spc;
1526 			}
1527 			break;
1528 		default:
1529 			break;
1530 		}
1531 		break;
1532 	default:
1533 		break;
1534 	}
1535 }
1536 
1537 static void
1538 post_lb(DECL_ARGS)
1539 {
1540 
1541 	if (SEC_LIBRARY == n->sec)
1542 		outflags |= MMAN_br;
1543 }
1544 
1545 static int
1546 pre_lk(DECL_ARGS)
1547 {
1548 	const struct roff_node *link, *descr, *punct;
1549 
1550 	if ((link = n->child) == NULL)
1551 		return 0;
1552 
1553 	/* Find beginning of trailing punctuation. */
1554 	punct = n->last;
1555 	while (punct != link && punct->flags & NODE_DELIMC)
1556 		punct = punct->prev;
1557 	punct = punct->next;
1558 
1559 	/* Link text. */
1560 	if ((descr = link->next) != NULL && descr != punct) {
1561 		font_push('I');
1562 		while (descr != punct) {
1563 			print_word(descr->string);
1564 			descr = descr->next;
1565 		}
1566 		font_pop();
1567 		print_word(":");
1568 	}
1569 
1570 	/* Link target. */
1571 	font_push('B');
1572 	print_word(link->string);
1573 	font_pop();
1574 
1575 	/* Trailing punctuation. */
1576 	while (punct != NULL) {
1577 		print_word(punct->string);
1578 		punct = punct->next;
1579 	}
1580 	return 0;
1581 }
1582 
1583 static void
1584 pre_onearg(DECL_ARGS)
1585 {
1586 	outflags |= MMAN_nl;
1587 	print_word(".");
1588 	outflags &= ~MMAN_spc;
1589 	print_word(roff_name[n->tok]);
1590 	if (n->child != NULL)
1591 		print_word(n->child->string);
1592 	outflags |= MMAN_nl;
1593 	if (n->tok == ROFF_ce)
1594 		for (n = n->child->next; n != NULL; n = n->next)
1595 			print_node(meta, n);
1596 }
1597 
1598 static int
1599 pre_li(DECL_ARGS)
1600 {
1601 
1602 	font_push('R');
1603 	return 1;
1604 }
1605 
1606 static int
1607 pre_nm(DECL_ARGS)
1608 {
1609 	char	*name;
1610 
1611 	if (n->type == ROFFT_BLOCK) {
1612 		outflags |= MMAN_Bk;
1613 		pre_syn(n);
1614 	}
1615 	if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD)
1616 		return 1;
1617 	name = n->child == NULL ? NULL : n->child->string;
1618 	if (NULL == name)
1619 		return 0;
1620 	if (n->type == ROFFT_HEAD) {
1621 		if (NULL == n->parent->prev)
1622 			outflags |= MMAN_sp;
1623 		print_block(".HP", 0);
1624 		printf(" %dn", man_strlen(name) + 1);
1625 		outflags |= MMAN_nl;
1626 	}
1627 	font_push('B');
1628 	return 1;
1629 }
1630 
1631 static void
1632 post_nm(DECL_ARGS)
1633 {
1634 
1635 	switch (n->type) {
1636 	case ROFFT_BLOCK:
1637 		outflags &= ~MMAN_Bk;
1638 		break;
1639 	case ROFFT_HEAD:
1640 	case ROFFT_ELEM:
1641 		if (n->child != NULL && n->child->string != NULL)
1642 			font_pop();
1643 		break;
1644 	default:
1645 		break;
1646 	}
1647 }
1648 
1649 static int
1650 pre_no(DECL_ARGS)
1651 {
1652 
1653 	outflags |= MMAN_spc_force;
1654 	return 1;
1655 }
1656 
1657 static int
1658 pre_ns(DECL_ARGS)
1659 {
1660 
1661 	outflags &= ~MMAN_spc;
1662 	return 0;
1663 }
1664 
1665 static void
1666 post_pf(DECL_ARGS)
1667 {
1668 
1669 	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1670 		outflags &= ~MMAN_spc;
1671 }
1672 
1673 static int
1674 pre_pp(DECL_ARGS)
1675 {
1676 
1677 	if (MDOC_It != n->parent->tok)
1678 		outflags |= MMAN_PP;
1679 	outflags |= MMAN_sp | MMAN_nl;
1680 	outflags &= ~MMAN_br;
1681 	return 0;
1682 }
1683 
1684 static int
1685 pre_rs(DECL_ARGS)
1686 {
1687 
1688 	if (SEC_SEE_ALSO == n->sec) {
1689 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1690 		outflags &= ~MMAN_br;
1691 	}
1692 	return 1;
1693 }
1694 
1695 static int
1696 pre_skip(DECL_ARGS)
1697 {
1698 
1699 	return 0;
1700 }
1701 
1702 static int
1703 pre_sm(DECL_ARGS)
1704 {
1705 
1706 	if (NULL == n->child)
1707 		outflags ^= MMAN_Sm;
1708 	else if (0 == strcmp("on", n->child->string))
1709 		outflags |= MMAN_Sm;
1710 	else
1711 		outflags &= ~MMAN_Sm;
1712 
1713 	if (MMAN_Sm & outflags)
1714 		outflags |= MMAN_spc;
1715 
1716 	return 0;
1717 }
1718 
1719 static void
1720 pre_sp(DECL_ARGS)
1721 {
1722 	if (outflags & MMAN_PP) {
1723 		outflags &= ~MMAN_PP;
1724 		print_line(".PP", 0);
1725 	} else {
1726 		print_line(".sp", 0);
1727 		if (n->child != NULL)
1728 			print_word(n->child->string);
1729 	}
1730 	outflags |= MMAN_nl;
1731 }
1732 
1733 static int
1734 pre_sy(DECL_ARGS)
1735 {
1736 
1737 	font_push('B');
1738 	return 1;
1739 }
1740 
1741 static void
1742 pre_ta(DECL_ARGS)
1743 {
1744 	print_line(".ta", 0);
1745 	for (n = n->child; n != NULL; n = n->next)
1746 		print_word(n->string);
1747 	outflags |= MMAN_nl;
1748 }
1749 
1750 static int
1751 pre_vt(DECL_ARGS)
1752 {
1753 
1754 	if (NODE_SYNPRETTY & n->flags) {
1755 		switch (n->type) {
1756 		case ROFFT_BLOCK:
1757 			pre_syn(n);
1758 			return 1;
1759 		case ROFFT_BODY:
1760 			break;
1761 		default:
1762 			return 0;
1763 		}
1764 	}
1765 	font_push('I');
1766 	return 1;
1767 }
1768 
1769 static void
1770 post_vt(DECL_ARGS)
1771 {
1772 
1773 	if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
1774 		return;
1775 	font_pop();
1776 }
1777 
1778 static int
1779 pre_xr(DECL_ARGS)
1780 {
1781 
1782 	n = n->child;
1783 	if (NULL == n)
1784 		return 0;
1785 	print_node(meta, n);
1786 	n = n->next;
1787 	if (NULL == n)
1788 		return 0;
1789 	outflags &= ~MMAN_spc;
1790 	print_word("(");
1791 	print_node(meta, n);
1792 	print_word(")");
1793 	return 0;
1794 }
1795