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