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