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