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