xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$Id: mdoc_man.c,v 1.34 2012/07/13 14:15:50 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011, 2012 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 <assert.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "mandoc.h"
22 #include "out.h"
23 #include "man.h"
24 #include "mdoc.h"
25 #include "main.h"
26 
27 #define	DECL_ARGS const struct mdoc_meta *m, \
28 		  const struct mdoc_node *n
29 
30 struct	manact {
31 	int		(*cond)(DECL_ARGS); /* DON'T run actions */
32 	int		(*pre)(DECL_ARGS); /* pre-node action */
33 	void		(*post)(DECL_ARGS); /* post-node action */
34 	const char	 *prefix; /* pre-node string constant */
35 	const char	 *suffix; /* post-node string constant */
36 };
37 
38 static	int	  cond_body(DECL_ARGS);
39 static	int	  cond_head(DECL_ARGS);
40 static  void	  font_push(char);
41 static	void	  font_pop(void);
42 static	void	  post__t(DECL_ARGS);
43 static	void	  post_bd(DECL_ARGS);
44 static	void	  post_bf(DECL_ARGS);
45 static	void	  post_bk(DECL_ARGS);
46 static	void	  post_bl(DECL_ARGS);
47 static	void	  post_dl(DECL_ARGS);
48 static	void	  post_enc(DECL_ARGS);
49 static	void	  post_eo(DECL_ARGS);
50 static	void	  post_fa(DECL_ARGS);
51 static	void	  post_fd(DECL_ARGS);
52 static	void	  post_fl(DECL_ARGS);
53 static	void	  post_fn(DECL_ARGS);
54 static	void	  post_fo(DECL_ARGS);
55 static	void	  post_font(DECL_ARGS);
56 static	void	  post_in(DECL_ARGS);
57 static	void	  post_it(DECL_ARGS);
58 static	void	  post_lb(DECL_ARGS);
59 static	void	  post_nm(DECL_ARGS);
60 static	void	  post_percent(DECL_ARGS);
61 static	void	  post_pf(DECL_ARGS);
62 static	void	  post_sect(DECL_ARGS);
63 static	void	  post_sp(DECL_ARGS);
64 static	void	  post_vt(DECL_ARGS);
65 static	int	  pre__t(DECL_ARGS);
66 static	int	  pre_an(DECL_ARGS);
67 static	int	  pre_ap(DECL_ARGS);
68 static	int	  pre_bd(DECL_ARGS);
69 static	int	  pre_bf(DECL_ARGS);
70 static	int	  pre_bk(DECL_ARGS);
71 static	int	  pre_bl(DECL_ARGS);
72 static	int	  pre_br(DECL_ARGS);
73 static	int	  pre_bx(DECL_ARGS);
74 static	int	  pre_dl(DECL_ARGS);
75 static	int	  pre_enc(DECL_ARGS);
76 static	int	  pre_em(DECL_ARGS);
77 static	int	  pre_fa(DECL_ARGS);
78 static	int	  pre_fd(DECL_ARGS);
79 static	int	  pre_fl(DECL_ARGS);
80 static	int	  pre_fn(DECL_ARGS);
81 static	int	  pre_fo(DECL_ARGS);
82 static	int	  pre_ft(DECL_ARGS);
83 static	int	  pre_in(DECL_ARGS);
84 static	int	  pre_it(DECL_ARGS);
85 static	int	  pre_lk(DECL_ARGS);
86 static	int	  pre_li(DECL_ARGS);
87 static	int	  pre_nm(DECL_ARGS);
88 static	int	  pre_no(DECL_ARGS);
89 static	int	  pre_ns(DECL_ARGS);
90 static	int	  pre_pp(DECL_ARGS);
91 static	int	  pre_rs(DECL_ARGS);
92 static	int	  pre_sm(DECL_ARGS);
93 static	int	  pre_sp(DECL_ARGS);
94 static	int	  pre_sect(DECL_ARGS);
95 static	int	  pre_sy(DECL_ARGS);
96 static	void	  pre_syn(const struct mdoc_node *);
97 static	int	  pre_vt(DECL_ARGS);
98 static	int	  pre_ux(DECL_ARGS);
99 static	int	  pre_xr(DECL_ARGS);
100 static	void	  print_word(const char *);
101 static	void	  print_line(const char *, int);
102 static	void	  print_block(const char *, int);
103 static	void	  print_offs(const char *);
104 static	void	  print_width(const char *,
105 				const struct mdoc_node *, size_t);
106 static	void	  print_count(int *);
107 static	void	  print_node(DECL_ARGS);
108 
109 static	const struct manact manacts[MDOC_MAX + 1] = {
110 	{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
111 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
112 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
113 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
114 	{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
115 	{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
116 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
117 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
118 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
119 	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
120 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
121 	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
122 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
123 	{ NULL, pre_it, post_it, NULL, NULL }, /* It */
124 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
125 	{ NULL, pre_an, NULL, NULL, NULL }, /* An */
126 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
127 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
128 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
129 	{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
130 	{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
131 	{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
132 	{ NULL, pre_enc, post_enc, "The \\fB",
133 	    "\\fP\nutility exits 0 on success, and >0 if an error occurs."
134 	    }, /* Ex */
135 	{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
136 	{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
137 	{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
138 	{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
139 	{ NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
140 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
141 	{ NULL, pre_in, post_in, NULL, NULL }, /* In */
142 	{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
143 	{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
144 	{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
145 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
146 	{ NULL, NULL, NULL, NULL, NULL }, /* Ot */
147 	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
148 	{ NULL, pre_enc, post_enc, "The \\fB",
149 		"\\fP\nfunction returns the value 0 if successful;\n"
150 		"otherwise the value -1 is returned and the global\n"
151 		"variable \\fIerrno\\fP is set to indicate the error."
152 		}, /* Rv */
153 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
154 	{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
155 	{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
156 	{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
157 	{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
158 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
159 	{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
160 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
161 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
162 	{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
163 	{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
164 	{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
165 	{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
166 	{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
167 	{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
168 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
169 	{ cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
170 	{ cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
171 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
172 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
173 	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
174 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
175 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
176 	{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
177 	{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
178 	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
179 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
180 	{ cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
181 	{ cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
182 	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
183 	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
184 	{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
185 	{ NULL, NULL, post_eo, NULL, NULL }, /* Eo */
186 	{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
187 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
188 	{ NULL, pre_no, NULL, NULL, NULL }, /* No */
189 	{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
190 	{ NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
191 	{ NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
192 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
193 	{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
194 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
195 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
196 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
197 	{ cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
198 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
199 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
200 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
201 	{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
202 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
203 	{ cond_body, pre_enc, post_enc, "`", "'" }, /* So */
204 	{ cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
205 	{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
206 	{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
207 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
208 	{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
209 	{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
210 	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
211 	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
212 	{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
213 	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
214 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
215 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
216 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
217 	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
218 	{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
219 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
220 	{ NULL, NULL, NULL, NULL, NULL }, /* Fr */
221 	{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
222 	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
223 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
224 	{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
225 	{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
226 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
227 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
228 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
229 	{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
230 	{ NULL, NULL, NULL, NULL, NULL }, /* Es */
231 	{ NULL, NULL, NULL, NULL, NULL }, /* En */
232 	{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
233 	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
234 	{ NULL, pre_br, NULL, NULL, NULL }, /* br */
235 	{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
236 	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
237 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
238 	{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
239 };
240 
241 static	int		outflags;
242 #define	MMAN_spc	(1 << 0)  /* blank character before next word */
243 #define	MMAN_spc_force	(1 << 1)  /* even before trailing punctuation */
244 #define	MMAN_nl		(1 << 2)  /* break man(7) code line */
245 #define	MMAN_br		(1 << 3)  /* break output line */
246 #define	MMAN_sp		(1 << 4)  /* insert a blank output line */
247 #define	MMAN_PP		(1 << 5)  /* reset indentation etc. */
248 #define	MMAN_Sm		(1 << 6)  /* horizontal spacing mode */
249 #define	MMAN_Bk		(1 << 7)  /* word keep mode */
250 #define	MMAN_An_split	(1 << 8)  /* author mode is "split" */
251 #define	MMAN_An_nosplit	(1 << 9)  /* author mode is "nosplit" */
252 
253 static	struct {
254 	char	*head;
255 	char	*tail;
256 	size_t	 size;
257 }	fontqueue;
258 
259 static void
260 font_push(char newfont)
261 {
262 
263 	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
264 		fontqueue.size += 8;
265 		fontqueue.head = mandoc_realloc(fontqueue.head,
266 				fontqueue.size);
267 	}
268 	*fontqueue.tail = newfont;
269 	print_word("\\f");
270 	putchar(newfont);
271 	outflags &= ~MMAN_spc;
272 }
273 
274 static void
275 font_pop(void)
276 {
277 
278 	if (fontqueue.tail > fontqueue.head)
279 		fontqueue.tail--;
280 	outflags &= ~MMAN_spc;
281 	print_word("\\f");
282 	putchar(*fontqueue.tail);
283 }
284 
285 static void
286 print_word(const char *s)
287 {
288 
289 	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
290 		/*
291 		 * If we need a newline, print it now and start afresh.
292 		 */
293 		if (MMAN_PP & outflags) {
294 			if ( ! (MMAN_sp & outflags))
295 				printf("\n.sp -1v");
296 			printf("\n.PP\n");
297 		} else if (MMAN_sp & outflags)
298 			printf("\n.sp\n");
299 		else if (MMAN_br & outflags)
300 			printf("\n.br\n");
301 		else if (MMAN_nl & outflags)
302 			putchar('\n');
303 		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
304 	} else if (MMAN_spc & outflags && '\0' != s[0])
305 		/*
306 		 * If we need a space, only print it if
307 		 * (1) it is forced by `No' or
308 		 * (2) what follows is not terminating punctuation or
309 		 * (3) what follows is longer than one character.
310 		 */
311 		if (MMAN_spc_force & outflags ||
312 		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
313 			if (MMAN_Bk & outflags) {
314 				putchar('\\');
315 				putchar('~');
316 			} else
317 				putchar(' ');
318 		}
319 
320 	/*
321 	 * Reassign needing space if we're not following opening
322 	 * punctuation.
323 	 */
324 	if (MMAN_Sm & outflags &&
325 	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1]))
326 		outflags |= MMAN_spc;
327 	else
328 		outflags &= ~MMAN_spc;
329 	outflags &= ~MMAN_spc_force;
330 
331 	for ( ; *s; s++) {
332 		switch (*s) {
333 		case (ASCII_NBRSP):
334 			printf("\\~");
335 			break;
336 		case (ASCII_HYPH):
337 			putchar('-');
338 			break;
339 		default:
340 			putchar((unsigned char)*s);
341 			break;
342 		}
343 	}
344 }
345 
346 static void
347 print_line(const char *s, int newflags)
348 {
349 
350 	outflags &= ~MMAN_br;
351 	outflags |= MMAN_nl;
352 	print_word(s);
353 	outflags |= newflags;
354 }
355 
356 static void
357 print_block(const char *s, int newflags)
358 {
359 
360 	outflags &= ~MMAN_PP;
361 	if (MMAN_sp & outflags)
362 		outflags &= ~(MMAN_sp | MMAN_br);
363 	else
364 		print_line(".sp -1v", 0);
365 	outflags |= MMAN_nl;
366 	print_word(s);
367 	outflags |= newflags;
368 }
369 
370 static void
371 print_offs(const char *v)
372 {
373 	char		  buf[24];
374 	struct roffsu	  su;
375 	size_t		  sz;
376 
377 	if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
378 		sz = 0;
379 	else if (0 == strcmp(v, "indent"))
380 		sz = 6;
381 	else if (0 == strcmp(v, "indent-two"))
382 		sz = 12;
383 	else if (a2roffsu(v, &su, SCALE_MAX)) {
384 		print_word(v);
385 		return;
386 	} else
387 		sz = strlen(v);
388 
389 	snprintf(buf, sizeof(buf), "%ldn", sz);
390 	print_word(buf);
391 }
392 
393 void
394 print_width(const char *v, const struct mdoc_node *child, size_t defsz)
395 {
396 	char		  buf[24];
397 	struct roffsu	  su;
398 	size_t		  sz, chsz;
399 
400 	/* XXX Rough estimation, might have multiple parts. */
401 	chsz = (NULL != child && MDOC_TEXT == child->type) ?
402 			strlen(child->string) : 0;
403 
404 	if (NULL == v)
405 		sz = defsz;
406 	else if (a2roffsu(v, &su, SCALE_MAX)) {
407 		if (SCALE_EN == su.unit)
408 			sz = su.scale;
409 		else {
410 			if (chsz)
411 				print_block(".HP", 0);
412 			else
413 				print_block(".TP", 0);
414 			print_word(v);
415 			return;
416 		}
417 	} else
418 		sz = strlen(v);
419 
420 	if (chsz > sz)
421 		print_block(".HP", 0);
422 	else
423 		print_block(".TP", 0);
424 	snprintf(buf, sizeof(buf), "%ldn", sz + 2);
425 	print_word(buf);
426 }
427 
428 void
429 print_count(int *count)
430 {
431 	char		  buf[12];
432 
433 	snprintf(buf, sizeof(buf), "%d.", ++*count);
434 	print_word(buf);
435 }
436 
437 void
438 man_man(void *arg, const struct man *man)
439 {
440 
441 	/*
442 	 * Dump the keep buffer.
443 	 * We're guaranteed by now that this exists (is non-NULL).
444 	 * Flush stdout afterward, just in case.
445 	 */
446 	fputs(mparse_getkeep(man_mparse(man)), stdout);
447 	fflush(stdout);
448 }
449 
450 void
451 man_mdoc(void *arg, const struct mdoc *mdoc)
452 {
453 	const struct mdoc_meta *m;
454 	const struct mdoc_node *n;
455 
456 	m = mdoc_meta(mdoc);
457 	n = mdoc_node(mdoc);
458 
459 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
460 			m->title, m->msec, m->date, m->os, m->vol);
461 
462 	outflags = MMAN_nl | MMAN_Sm;
463 	if (0 == fontqueue.size) {
464 		fontqueue.size = 8;
465 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
466 		*fontqueue.tail = 'R';
467 	}
468 	print_node(m, n);
469 	putchar('\n');
470 }
471 
472 static void
473 print_node(DECL_ARGS)
474 {
475 	const struct mdoc_node	*prev, *sub;
476 	const struct manact	*act;
477 	int			 cond, do_sub;
478 
479 	/*
480 	 * Break the line if we were parsed subsequent the current node.
481 	 * This makes the page structure be more consistent.
482 	 */
483 	prev = n->prev ? n->prev : n->parent;
484 	if (MMAN_spc & outflags && prev && prev->line < n->line)
485 		outflags |= MMAN_nl;
486 
487 	act = NULL;
488 	cond = 0;
489 	do_sub = 1;
490 
491 	if (MDOC_TEXT == n->type) {
492 		/*
493 		 * Make sure that we don't happen to start with a
494 		 * control character at the start of a line.
495 		 */
496 		if (MMAN_nl & outflags && ('.' == *n->string ||
497 					'\'' == *n->string)) {
498 			print_word("\\&");
499 			outflags &= ~MMAN_spc;
500 		}
501 		print_word(n->string);
502 	} else {
503 		/*
504 		 * Conditionally run the pre-node action handler for a
505 		 * node.
506 		 */
507 		act = manacts + n->tok;
508 		cond = NULL == act->cond || (*act->cond)(m, n);
509 		if (cond && act->pre)
510 			do_sub = (*act->pre)(m, n);
511 	}
512 
513 	/*
514 	 * Conditionally run all child nodes.
515 	 * Note that this iterates over children instead of using
516 	 * recursion.  This prevents unnecessary depth in the stack.
517 	 */
518 	if (do_sub)
519 		for (sub = n->child; sub; sub = sub->next)
520 			print_node(m, sub);
521 
522 	/*
523 	 * Lastly, conditionally run the post-node handler.
524 	 */
525 	if (cond && act->post)
526 		(*act->post)(m, n);
527 }
528 
529 static int
530 cond_head(DECL_ARGS)
531 {
532 
533 	return(MDOC_HEAD == n->type);
534 }
535 
536 static int
537 cond_body(DECL_ARGS)
538 {
539 
540 	return(MDOC_BODY == n->type);
541 }
542 
543 static int
544 pre_enc(DECL_ARGS)
545 {
546 	const char	*prefix;
547 
548 	prefix = manacts[n->tok].prefix;
549 	if (NULL == prefix)
550 		return(1);
551 	print_word(prefix);
552 	outflags &= ~MMAN_spc;
553 	return(1);
554 }
555 
556 static void
557 post_enc(DECL_ARGS)
558 {
559 	const char *suffix;
560 
561 	suffix = manacts[n->tok].suffix;
562 	if (NULL == suffix)
563 		return;
564 	outflags &= ~MMAN_spc;
565 	print_word(suffix);
566 }
567 
568 static void
569 post_font(DECL_ARGS)
570 {
571 
572 	font_pop();
573 }
574 
575 static void
576 post_percent(DECL_ARGS)
577 {
578 
579 	if (pre_em == manacts[n->tok].pre)
580 		font_pop();
581 	if (n->next) {
582 		print_word(",");
583 		if (n->prev &&	n->prev->tok == n->tok &&
584 				n->next->tok == n->tok)
585 			print_word("and");
586 	} else {
587 		print_word(".");
588 		outflags |= MMAN_nl;
589 	}
590 }
591 
592 static int
593 pre__t(DECL_ARGS)
594 {
595 
596         if (n->parent && MDOC_Rs == n->parent->tok &&
597                         n->parent->norm->Rs.quote_T) {
598 		print_word("\"");
599 		outflags &= ~MMAN_spc;
600 	} else
601 		font_push('I');
602 	return(1);
603 }
604 
605 static void
606 post__t(DECL_ARGS)
607 {
608 
609         if (n->parent && MDOC_Rs == n->parent->tok &&
610                         n->parent->norm->Rs.quote_T) {
611 		outflags &= ~MMAN_spc;
612 		print_word("\"");
613 	} else
614 		font_pop();
615 	post_percent(m, n);
616 }
617 
618 /*
619  * Print before a section header.
620  */
621 static int
622 pre_sect(DECL_ARGS)
623 {
624 
625 	if (MDOC_HEAD != n->type)
626 		return(1);
627 	outflags |= MMAN_sp;
628 	print_block(manacts[n->tok].prefix, 0);
629 	print_word("\"");
630 	outflags &= ~MMAN_spc;
631 	return(1);
632 }
633 
634 /*
635  * Print subsequent a section header.
636  */
637 static void
638 post_sect(DECL_ARGS)
639 {
640 
641 	if (MDOC_HEAD != n->type)
642 		return;
643 	outflags &= ~MMAN_spc;
644 	print_word("\"");
645 	outflags |= MMAN_nl;
646 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
647 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
648 }
649 
650 /* See mdoc_term.c, synopsis_pre() for comments. */
651 static void
652 pre_syn(const struct mdoc_node *n)
653 {
654 
655 	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
656 		return;
657 
658 	if (n->prev->tok == n->tok &&
659 			MDOC_Ft != n->tok &&
660 			MDOC_Fo != n->tok &&
661 			MDOC_Fn != n->tok) {
662 		outflags |= MMAN_br;
663 		return;
664 	}
665 
666 	switch (n->prev->tok) {
667 	case (MDOC_Fd):
668 		/* FALLTHROUGH */
669 	case (MDOC_Fn):
670 		/* FALLTHROUGH */
671 	case (MDOC_Fo):
672 		/* FALLTHROUGH */
673 	case (MDOC_In):
674 		/* FALLTHROUGH */
675 	case (MDOC_Vt):
676 		outflags |= MMAN_sp;
677 		break;
678 	case (MDOC_Ft):
679 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
680 			outflags |= MMAN_sp;
681 			break;
682 		}
683 		/* FALLTHROUGH */
684 	default:
685 		outflags |= MMAN_br;
686 		break;
687 	}
688 }
689 
690 static int
691 pre_an(DECL_ARGS)
692 {
693 
694 	switch (n->norm->An.auth) {
695 	case (AUTH_split):
696 		outflags &= ~MMAN_An_nosplit;
697 		outflags |= MMAN_An_split;
698 		return(0);
699 	case (AUTH_nosplit):
700 		outflags &= ~MMAN_An_split;
701 		outflags |= MMAN_An_nosplit;
702 		return(0);
703 	default:
704 		if (MMAN_An_split & outflags)
705 			outflags |= MMAN_br;
706 		else if (SEC_AUTHORS == n->sec &&
707 		    ! (MMAN_An_nosplit & outflags))
708 			outflags |= MMAN_An_split;
709 		return(1);
710 	}
711 }
712 
713 static int
714 pre_ap(DECL_ARGS)
715 {
716 
717 	outflags &= ~MMAN_spc;
718 	print_word("'");
719 	outflags &= ~MMAN_spc;
720 	return(0);
721 }
722 
723 static int
724 pre_bd(DECL_ARGS)
725 {
726 
727 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
728 
729 	if (DISP_unfilled == n->norm->Bd.type ||
730 	    DISP_literal  == n->norm->Bd.type)
731 		print_line(".nf", 0);
732 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
733 		outflags |= MMAN_sp;
734 	print_line(".RS", 0);
735 	print_offs(n->norm->Bd.offs);
736 	outflags |= MMAN_nl;
737 	return(1);
738 }
739 
740 static void
741 post_bd(DECL_ARGS)
742 {
743 
744 	print_line(".RE", MMAN_nl);
745 	if (DISP_unfilled == n->norm->Bd.type ||
746 	    DISP_literal  == n->norm->Bd.type)
747 		print_line(".fi", MMAN_nl);
748 }
749 
750 static int
751 pre_bf(DECL_ARGS)
752 {
753 
754 	switch (n->type) {
755 	case (MDOC_BLOCK):
756 		return(1);
757 	case (MDOC_BODY):
758 		break;
759 	default:
760 		return(0);
761 	}
762 	switch (n->norm->Bf.font) {
763 	case (FONT_Em):
764 		font_push('I');
765 		break;
766 	case (FONT_Sy):
767 		font_push('B');
768 		break;
769 	default:
770 		font_push('R');
771 		break;
772 	}
773 	return(1);
774 }
775 
776 static void
777 post_bf(DECL_ARGS)
778 {
779 
780 	if (MDOC_BODY == n->type)
781 		font_pop();
782 }
783 
784 static int
785 pre_bk(DECL_ARGS)
786 {
787 
788 	switch (n->type) {
789 	case (MDOC_BLOCK):
790 		return(1);
791 	case (MDOC_BODY):
792 		outflags |= MMAN_Bk;
793 		return(1);
794 	default:
795 		return(0);
796 	}
797 }
798 
799 static void
800 post_bk(DECL_ARGS)
801 {
802 
803 	if (MDOC_BODY == n->type)
804 		outflags &= ~MMAN_Bk;
805 }
806 
807 static int
808 pre_bl(DECL_ARGS)
809 {
810 	size_t		 icol;
811 
812 	switch (n->norm->Bl.type) {
813 	case (LIST_enum):
814 		n->norm->Bl.count = 0;
815 		return(1);
816 	case (LIST_column):
817 		break;
818 	default:
819 		return(1);
820 	}
821 
822 	print_line(".TS", MMAN_nl);
823 	for (icol = 0; icol < n->norm->Bl.ncols; icol++)
824 		print_word("l");
825 	print_word(".");
826 	outflags |= MMAN_nl;
827 	return(1);
828 }
829 
830 static void
831 post_bl(DECL_ARGS)
832 {
833 
834 	switch (n->norm->Bl.type) {
835 	case (LIST_column):
836 		print_line(".TE", 0);
837 		break;
838 	case (LIST_enum):
839 		n->norm->Bl.count = 0;
840 		break;
841 	default:
842 		break;
843 	}
844 	outflags |= MMAN_PP | MMAN_nl;
845 	outflags &= ~(MMAN_sp | MMAN_br);
846 }
847 
848 static int
849 pre_br(DECL_ARGS)
850 {
851 
852 	outflags |= MMAN_br;
853 	return(0);
854 }
855 
856 static int
857 pre_bx(DECL_ARGS)
858 {
859 
860 	n = n->child;
861 	if (n) {
862 		print_word(n->string);
863 		outflags &= ~MMAN_spc;
864 		n = n->next;
865 	}
866 	print_word("BSD");
867 	if (NULL == n)
868 		return(0);
869 	outflags &= ~MMAN_spc;
870 	print_word("-");
871 	outflags &= ~MMAN_spc;
872 	print_word(n->string);
873 	return(0);
874 }
875 
876 static int
877 pre_dl(DECL_ARGS)
878 {
879 
880 	print_line(".RS 6n", MMAN_nl);
881 	return(1);
882 }
883 
884 static void
885 post_dl(DECL_ARGS)
886 {
887 
888 	print_line(".RE", MMAN_nl);
889 }
890 
891 static int
892 pre_em(DECL_ARGS)
893 {
894 
895 	font_push('I');
896 	return(1);
897 }
898 
899 static void
900 post_eo(DECL_ARGS)
901 {
902 
903 	if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
904 		outflags &= ~MMAN_spc;
905 }
906 
907 static int
908 pre_fa(DECL_ARGS)
909 {
910 
911 	if (MDOC_Fa == n->tok)
912 		n = n->child;
913 
914 	while (NULL != n) {
915 		font_push('I');
916 		print_node(m, n);
917 		font_pop();
918 		if (NULL != (n = n->next))
919 			print_word(",");
920 	}
921 	return(0);
922 }
923 
924 static void
925 post_fa(DECL_ARGS)
926 {
927 
928 	if (NULL != n->next && MDOC_Fa == n->next->tok)
929 		print_word(",");
930 }
931 
932 static int
933 pre_fd(DECL_ARGS)
934 {
935 
936 	pre_syn(n);
937 	font_push('B');
938 	return(1);
939 }
940 
941 static void
942 post_fd(DECL_ARGS)
943 {
944 
945 	font_pop();
946 	outflags |= MMAN_br;
947 }
948 
949 static int
950 pre_fl(DECL_ARGS)
951 {
952 
953 	font_push('B');
954 	print_word("-");
955 	outflags &= ~MMAN_spc;
956 	return(1);
957 }
958 
959 static void
960 post_fl(DECL_ARGS)
961 {
962 
963 	font_pop();
964 	if (0 == n->nchild && NULL != n->next &&
965 			n->next->line == n->line)
966 		outflags &= ~MMAN_spc;
967 }
968 
969 static int
970 pre_fn(DECL_ARGS)
971 {
972 
973 	pre_syn(n);
974 
975 	n = n->child;
976 	if (NULL == n)
977 		return(0);
978 
979 	font_push('B');
980 	print_node(m, n);
981 	font_pop();
982 	outflags &= ~MMAN_spc;
983 	print_word("(");
984 	outflags &= ~MMAN_spc;
985 
986 	n = n->next;
987 	if (NULL != n)
988 		pre_fa(m, n);
989 	return(0);
990 }
991 
992 static void
993 post_fn(DECL_ARGS)
994 {
995 
996 	print_word(")");
997 	if (MDOC_SYNPRETTY & n->flags) {
998 		print_word(";");
999 		outflags |= MMAN_br;
1000 	}
1001 }
1002 
1003 static int
1004 pre_fo(DECL_ARGS)
1005 {
1006 
1007 	switch (n->type) {
1008 	case (MDOC_BLOCK):
1009 		pre_syn(n);
1010 		break;
1011 	case (MDOC_HEAD):
1012 		font_push('B');
1013 		break;
1014 	case (MDOC_BODY):
1015 		outflags &= ~MMAN_spc;
1016 		print_word("(");
1017 		outflags &= ~MMAN_spc;
1018 		break;
1019 	default:
1020 		break;
1021 	}
1022 	return(1);
1023 }
1024 
1025 static void
1026 post_fo(DECL_ARGS)
1027 {
1028 
1029 	switch (n->type) {
1030 	case (MDOC_HEAD):
1031 		font_pop();
1032 		break;
1033 	case (MDOC_BODY):
1034 		post_fn(m, n);
1035 		break;
1036 	default:
1037 		break;
1038 	}
1039 }
1040 
1041 static int
1042 pre_ft(DECL_ARGS)
1043 {
1044 
1045 	pre_syn(n);
1046 	font_push('I');
1047 	return(1);
1048 }
1049 
1050 static int
1051 pre_in(DECL_ARGS)
1052 {
1053 
1054 	if (MDOC_SYNPRETTY & n->flags) {
1055 		pre_syn(n);
1056 		font_push('B');
1057 		print_word("#include <");
1058 		outflags &= ~MMAN_spc;
1059 	} else {
1060 		print_word("<");
1061 		outflags &= ~MMAN_spc;
1062 		font_push('I');
1063 	}
1064 	return(1);
1065 }
1066 
1067 static void
1068 post_in(DECL_ARGS)
1069 {
1070 
1071 	if (MDOC_SYNPRETTY & n->flags) {
1072 		outflags &= ~MMAN_spc;
1073 		print_word(">");
1074 		font_pop();
1075 		outflags |= MMAN_br;
1076 	} else {
1077 		font_pop();
1078 		outflags &= ~MMAN_spc;
1079 		print_word(">");
1080 	}
1081 }
1082 
1083 static int
1084 pre_it(DECL_ARGS)
1085 {
1086 	const struct mdoc_node *bln;
1087 
1088 	switch (n->type) {
1089 	case (MDOC_HEAD):
1090 		outflags |= MMAN_PP | MMAN_nl;
1091 		bln = n->parent->parent;
1092 		if (0 == bln->norm->Bl.comp ||
1093 		    NULL == bln->parent->prev)
1094 			outflags |= MMAN_sp;
1095 		outflags &= ~MMAN_br;
1096 		switch (bln->norm->Bl.type) {
1097 		case (LIST_item):
1098 			return(0);
1099 		case (LIST_inset):
1100 			/* FALLTHROUGH */
1101 		case (LIST_diag):
1102 			/* FALLTHROUGH */
1103 		case (LIST_ohang):
1104 			if (bln->norm->Bl.type == LIST_diag)
1105 				print_line(".B \"", 0);
1106 			else
1107 				print_line(".R \"", 0);
1108 			outflags &= ~MMAN_spc;
1109 			return(1);
1110 		case (LIST_bullet):
1111 			/* FALLTHROUGH */
1112 		case (LIST_dash):
1113 			/* FALLTHROUGH */
1114 		case (LIST_hyphen):
1115 			print_width(bln->norm->Bl.width, NULL, 0);
1116 			outflags |= MMAN_nl;
1117 			font_push('B');
1118 			if (LIST_bullet == bln->norm->Bl.type)
1119 				print_word("o");
1120 			else
1121 				print_word("-");
1122 			font_pop();
1123 			break;
1124 		case (LIST_enum):
1125 			print_width(bln->norm->Bl.width, NULL, 0);
1126 			outflags |= MMAN_nl;
1127 			print_count(&bln->norm->Bl.count);
1128 			break;
1129 		case (LIST_hang):
1130 			print_width(bln->norm->Bl.width, n->child, 6);
1131 			break;
1132 		case (LIST_tag):
1133 			print_width(bln->norm->Bl.width, NULL, 8);
1134 			break;
1135 		default:
1136 			return(1);
1137 		}
1138 		outflags |= MMAN_nl;
1139 	default:
1140 		break;
1141 	}
1142 	return(1);
1143 }
1144 
1145 static void
1146 post_it(DECL_ARGS)
1147 {
1148 	const struct mdoc_node *bln;
1149 
1150 	bln = n->parent->parent;
1151 
1152 	switch (n->type) {
1153 	case (MDOC_HEAD):
1154 		switch (bln->norm->Bl.type) {
1155 		case (LIST_diag):
1156 			outflags &= ~MMAN_spc;
1157 			print_word("\\ ");
1158 			break;
1159 		case (LIST_ohang):
1160 			outflags |= MMAN_br;
1161 			break;
1162 		default:
1163 			break;
1164 		}
1165 		break;
1166 	case (MDOC_BODY):
1167 		if (LIST_column == bln->norm->Bl.type &&
1168 		    NULL != n->next) {
1169 			putchar('\t');
1170 			outflags &= ~MMAN_spc;
1171 		}
1172 		break;
1173 	default:
1174 		break;
1175 	}
1176 }
1177 
1178 static void
1179 post_lb(DECL_ARGS)
1180 {
1181 
1182 	if (SEC_LIBRARY == n->sec)
1183 		outflags |= MMAN_br;
1184 }
1185 
1186 static int
1187 pre_lk(DECL_ARGS)
1188 {
1189 	const struct mdoc_node *link, *descr;
1190 
1191 	if (NULL == (link = n->child))
1192 		return(0);
1193 
1194 	if (NULL != (descr = link->next)) {
1195 		font_push('I');
1196 		while (NULL != descr) {
1197 			print_word(descr->string);
1198 			descr = descr->next;
1199 		}
1200 		print_word(":");
1201 		font_pop();
1202 	}
1203 
1204 	font_push('B');
1205 	print_word(link->string);
1206 	font_pop();
1207 	return(0);
1208 }
1209 
1210 static int
1211 pre_li(DECL_ARGS)
1212 {
1213 
1214 	font_push('R');
1215 	return(1);
1216 }
1217 
1218 static int
1219 pre_nm(DECL_ARGS)
1220 {
1221 
1222 	if (MDOC_BLOCK == n->type)
1223 		pre_syn(n);
1224 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1225 		return(1);
1226 	if (NULL == n->child && NULL == m->name)
1227 		return(0);
1228 	font_push('B');
1229 	if (NULL == n->child)
1230 		print_word(m->name);
1231 	return(1);
1232 }
1233 
1234 static void
1235 post_nm(DECL_ARGS)
1236 {
1237 
1238 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1239 		return;
1240 	font_pop();
1241 }
1242 
1243 static int
1244 pre_no(DECL_ARGS)
1245 {
1246 
1247 	outflags |= MMAN_spc_force;
1248 	return(1);
1249 }
1250 
1251 static int
1252 pre_ns(DECL_ARGS)
1253 {
1254 
1255 	outflags &= ~MMAN_spc;
1256 	return(0);
1257 }
1258 
1259 static void
1260 post_pf(DECL_ARGS)
1261 {
1262 
1263 	outflags &= ~MMAN_spc;
1264 }
1265 
1266 static int
1267 pre_pp(DECL_ARGS)
1268 {
1269 
1270 	if (MDOC_It != n->parent->tok)
1271 		outflags |= MMAN_PP;
1272 	outflags |= MMAN_sp | MMAN_nl;
1273 	outflags &= ~MMAN_br;
1274 	return(0);
1275 }
1276 
1277 static int
1278 pre_rs(DECL_ARGS)
1279 {
1280 
1281 	if (SEC_SEE_ALSO == n->sec) {
1282 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1283 		outflags &= ~MMAN_br;
1284 	}
1285 	return(1);
1286 }
1287 
1288 static int
1289 pre_sm(DECL_ARGS)
1290 {
1291 
1292 	assert(n->child && MDOC_TEXT == n->child->type);
1293 	if (0 == strcmp("on", n->child->string))
1294 		outflags |= MMAN_Sm | MMAN_spc;
1295 	else
1296 		outflags &= ~MMAN_Sm;
1297 	return(0);
1298 }
1299 
1300 static int
1301 pre_sp(DECL_ARGS)
1302 {
1303 
1304 	print_line(".sp", MMAN_nl);
1305 	return(1);
1306 }
1307 
1308 static void
1309 post_sp(DECL_ARGS)
1310 {
1311 
1312 	outflags |= MMAN_nl;
1313 }
1314 
1315 static int
1316 pre_sy(DECL_ARGS)
1317 {
1318 
1319 	font_push('B');
1320 	return(1);
1321 }
1322 
1323 static int
1324 pre_vt(DECL_ARGS)
1325 {
1326 
1327 	if (MDOC_SYNPRETTY & n->flags) {
1328 		switch (n->type) {
1329 		case (MDOC_BLOCK):
1330 			pre_syn(n);
1331 			return(1);
1332 		case (MDOC_BODY):
1333 			break;
1334 		default:
1335 			return(0);
1336 		}
1337 	}
1338 	font_push('I');
1339 	return(1);
1340 }
1341 
1342 static void
1343 post_vt(DECL_ARGS)
1344 {
1345 
1346 	if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1347 		return;
1348 	font_pop();
1349 }
1350 
1351 static int
1352 pre_xr(DECL_ARGS)
1353 {
1354 
1355 	n = n->child;
1356 	if (NULL == n)
1357 		return(0);
1358 	print_node(m, n);
1359 	n = n->next;
1360 	if (NULL == n)
1361 		return(0);
1362 	outflags &= ~MMAN_spc;
1363 	print_word("(");
1364 	print_node(m, n);
1365 	print_word(")");
1366 	return(0);
1367 }
1368 
1369 static int
1370 pre_ux(DECL_ARGS)
1371 {
1372 
1373 	print_word(manacts[n->tok].prefix);
1374 	if (NULL == n->child)
1375 		return(0);
1376 	outflags &= ~MMAN_spc;
1377 	print_word("\\~");
1378 	outflags &= ~MMAN_spc;
1379 	return(1);
1380 }
1381