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