xref: /openbsd-src/usr.bin/mandoc/mdoc_man.c (revision aa5e9e10509ffd51558f081f01cd78bfa3c4f2a5)
1 /*	$Id: mdoc_man.c,v 1.50 2013/06/02 18:16:51 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 #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, "\\(lq", "\\(rq" }, /* Do */
182 	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* 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, "\\(oq", "\\(cq" }, /* 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, "\\(oq", "\\(cq" }, /* So */
205 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* 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 	print_line(".RS", MMAN_Bk_susp);
408 
409 	/* Convert v into a number (of characters). */
410 	if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
411 		sz = 0;
412 	else if (0 == strcmp(v, "indent"))
413 		sz = 6;
414 	else if (0 == strcmp(v, "indent-two"))
415 		sz = 12;
416 	else if (a2roffsu(v, &su, SCALE_MAX)) {
417 		if (SCALE_EN == su.unit)
418 			sz = su.scale;
419 		else {
420 			/*
421 			 * XXX
422 			 * If we are inside an enclosing list,
423 			 * there is no easy way to add the two
424 			 * indentations because they are provided
425 			 * in terms of different units.
426 			 */
427 			print_word(v);
428 			outflags |= MMAN_nl;
429 			return;
430 		}
431 	} else
432 		sz = strlen(v);
433 
434 	/*
435 	 * We are inside an enclosing list.
436 	 * Add the two indentations.
437 	 */
438 	if (Bl_stack_len)
439 		sz += Bl_stack[Bl_stack_len - 1];
440 
441 	snprintf(buf, sizeof(buf), "%ldn", sz);
442 	print_word(buf);
443 	outflags |= MMAN_nl;
444 }
445 
446 /*
447  * Set up the indentation for a list item; used from pre_it().
448  */
449 void
450 print_width(const char *v, const struct mdoc_node *child, size_t defsz)
451 {
452 	char		  buf[24];
453 	struct roffsu	  su;
454 	size_t		  sz, chsz;
455 	int		  numeric, remain;
456 
457 	numeric = 1;
458 	remain = 0;
459 
460 	/* Convert v into a number (of characters). */
461 	if (NULL == v)
462 		sz = defsz;
463 	else if (a2roffsu(v, &su, SCALE_MAX)) {
464 		if (SCALE_EN == su.unit)
465 			sz = su.scale;
466 		else {
467 			sz = 0;
468 			numeric = 0;
469 		}
470 	} else
471 		sz = strlen(v);
472 
473 	/* XXX Rough estimation, might have multiple parts. */
474 	chsz = (NULL != child && MDOC_TEXT == child->type) ?
475 			strlen(child->string) : 0;
476 
477 	/* Maybe we are inside an enclosing list? */
478 	mid_it();
479 
480 	/*
481 	 * Save our own indentation,
482 	 * such that child lists can use it.
483 	 */
484 	Bl_stack[Bl_stack_len++] = sz + 2;
485 
486 	/* Set up the current list. */
487 	if (defsz && chsz > sz)
488 		print_block(".HP", 0);
489 	else {
490 		print_block(".TP", 0);
491 		remain = sz + 2;
492 	}
493 	if (numeric) {
494 		snprintf(buf, sizeof(buf), "%ldn", sz + 2);
495 		print_word(buf);
496 	} else
497 		print_word(v);
498 	TPremain = remain;
499 }
500 
501 void
502 print_count(int *count)
503 {
504 	char		  buf[12];
505 
506 	snprintf(buf, sizeof(buf), "%d.", ++*count);
507 	print_word(buf);
508 }
509 
510 void
511 man_man(void *arg, const struct man *man)
512 {
513 
514 	/*
515 	 * Dump the keep buffer.
516 	 * We're guaranteed by now that this exists (is non-NULL).
517 	 * Flush stdout afterward, just in case.
518 	 */
519 	fputs(mparse_getkeep(man_mparse(man)), stdout);
520 	fflush(stdout);
521 }
522 
523 void
524 man_mdoc(void *arg, const struct mdoc *mdoc)
525 {
526 	const struct mdoc_meta *meta;
527 	const struct mdoc_node *n;
528 
529 	meta = mdoc_meta(mdoc);
530 	n = mdoc_node(mdoc);
531 
532 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
533 			meta->title, meta->msec, meta->date,
534 			meta->os, meta->vol);
535 
536 	/* Disable hyphenation and if nroff, disable justification. */
537 	printf(".nh\n.if n .ad l");
538 
539 	outflags = MMAN_nl | MMAN_Sm;
540 	if (0 == fontqueue.size) {
541 		fontqueue.size = 8;
542 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
543 		*fontqueue.tail = 'R';
544 	}
545 	print_node(meta, n);
546 	putchar('\n');
547 }
548 
549 static void
550 print_node(DECL_ARGS)
551 {
552 	const struct mdoc_node	*prev, *sub;
553 	const struct manact	*act;
554 	int			 cond, do_sub;
555 
556 	/*
557 	 * Break the line if we were parsed subsequent the current node.
558 	 * This makes the page structure be more consistent.
559 	 */
560 	prev = n->prev ? n->prev : n->parent;
561 	if (MMAN_spc & outflags && prev && prev->line < n->line)
562 		outflags |= MMAN_nl;
563 
564 	act = NULL;
565 	cond = 0;
566 	do_sub = 1;
567 
568 	if (MDOC_TEXT == n->type) {
569 		/*
570 		 * Make sure that we don't happen to start with a
571 		 * control character at the start of a line.
572 		 */
573 		if (MMAN_nl & outflags && ('.' == *n->string ||
574 					'\'' == *n->string)) {
575 			print_word("");
576 			printf("\\&");
577 			outflags &= ~MMAN_spc;
578 		}
579 		print_word(n->string);
580 	} else {
581 		/*
582 		 * Conditionally run the pre-node action handler for a
583 		 * node.
584 		 */
585 		act = manacts + n->tok;
586 		cond = NULL == act->cond || (*act->cond)(meta, n);
587 		if (cond && act->pre)
588 			do_sub = (*act->pre)(meta, n);
589 	}
590 
591 	/*
592 	 * Conditionally run all child nodes.
593 	 * Note that this iterates over children instead of using
594 	 * recursion.  This prevents unnecessary depth in the stack.
595 	 */
596 	if (do_sub)
597 		for (sub = n->child; sub; sub = sub->next)
598 			print_node(meta, sub);
599 
600 	/*
601 	 * Lastly, conditionally run the post-node handler.
602 	 */
603 	if (cond && act->post)
604 		(*act->post)(meta, n);
605 }
606 
607 static int
608 cond_head(DECL_ARGS)
609 {
610 
611 	return(MDOC_HEAD == n->type);
612 }
613 
614 static int
615 cond_body(DECL_ARGS)
616 {
617 
618 	return(MDOC_BODY == n->type);
619 }
620 
621 static int
622 pre_enc(DECL_ARGS)
623 {
624 	const char	*prefix;
625 
626 	prefix = manacts[n->tok].prefix;
627 	if (NULL == prefix)
628 		return(1);
629 	print_word(prefix);
630 	outflags &= ~MMAN_spc;
631 	return(1);
632 }
633 
634 static void
635 post_enc(DECL_ARGS)
636 {
637 	const char *suffix;
638 
639 	suffix = manacts[n->tok].suffix;
640 	if (NULL == suffix)
641 		return;
642 	outflags &= ~MMAN_spc;
643 	print_word(suffix);
644 }
645 
646 static void
647 post_font(DECL_ARGS)
648 {
649 
650 	font_pop();
651 }
652 
653 static void
654 post_percent(DECL_ARGS)
655 {
656 
657 	if (pre_em == manacts[n->tok].pre)
658 		font_pop();
659 	if (n->next) {
660 		print_word(",");
661 		if (n->prev &&	n->prev->tok == n->tok &&
662 				n->next->tok == n->tok)
663 			print_word("and");
664 	} else {
665 		print_word(".");
666 		outflags |= MMAN_nl;
667 	}
668 }
669 
670 static int
671 pre__t(DECL_ARGS)
672 {
673 
674         if (n->parent && MDOC_Rs == n->parent->tok &&
675                         n->parent->norm->Rs.quote_T) {
676 		print_word("");
677 		putchar('\"');
678 		outflags &= ~MMAN_spc;
679 	} else
680 		font_push('I');
681 	return(1);
682 }
683 
684 static void
685 post__t(DECL_ARGS)
686 {
687 
688         if (n->parent && MDOC_Rs == n->parent->tok &&
689                         n->parent->norm->Rs.quote_T) {
690 		outflags &= ~MMAN_spc;
691 		print_word("");
692 		putchar('\"');
693 	} else
694 		font_pop();
695 	post_percent(meta, n);
696 }
697 
698 /*
699  * Print before a section header.
700  */
701 static int
702 pre_sect(DECL_ARGS)
703 {
704 
705 	switch (n->type) {
706 	case (MDOC_HEAD):
707 		outflags |= MMAN_sp;
708 		print_block(manacts[n->tok].prefix, 0);
709 		print_word("");
710 		putchar('\"');
711 		outflags &= ~MMAN_spc;
712 		break;
713 	case (MDOC_BODY):
714 		if (MDOC_Sh == n->tok) {
715 			if (MDOC_SYNPRETTY & n->flags)
716 				outflags |= MMAN_Bk;
717 			else
718 				outflags &= ~MMAN_Bk;
719 		}
720 		break;
721 	default:
722 		break;
723 	}
724 	return(1);
725 }
726 
727 /*
728  * Print subsequent a section header.
729  */
730 static void
731 post_sect(DECL_ARGS)
732 {
733 
734 	if (MDOC_HEAD != n->type)
735 		return;
736 	outflags &= ~MMAN_spc;
737 	print_word("");
738 	putchar('\"');
739 	outflags |= MMAN_nl;
740 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
741 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
742 }
743 
744 /* See mdoc_term.c, synopsis_pre() for comments. */
745 static void
746 pre_syn(const struct mdoc_node *n)
747 {
748 
749 	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
750 		return;
751 
752 	if (n->prev->tok == n->tok &&
753 			MDOC_Ft != n->tok &&
754 			MDOC_Fo != n->tok &&
755 			MDOC_Fn != n->tok) {
756 		outflags |= MMAN_br;
757 		return;
758 	}
759 
760 	switch (n->prev->tok) {
761 	case (MDOC_Fd):
762 		/* FALLTHROUGH */
763 	case (MDOC_Fn):
764 		/* FALLTHROUGH */
765 	case (MDOC_Fo):
766 		/* FALLTHROUGH */
767 	case (MDOC_In):
768 		/* FALLTHROUGH */
769 	case (MDOC_Vt):
770 		outflags |= MMAN_sp;
771 		break;
772 	case (MDOC_Ft):
773 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
774 			outflags |= MMAN_sp;
775 			break;
776 		}
777 		/* FALLTHROUGH */
778 	default:
779 		outflags |= MMAN_br;
780 		break;
781 	}
782 }
783 
784 static int
785 pre_an(DECL_ARGS)
786 {
787 
788 	switch (n->norm->An.auth) {
789 	case (AUTH_split):
790 		outflags &= ~MMAN_An_nosplit;
791 		outflags |= MMAN_An_split;
792 		return(0);
793 	case (AUTH_nosplit):
794 		outflags &= ~MMAN_An_split;
795 		outflags |= MMAN_An_nosplit;
796 		return(0);
797 	default:
798 		if (MMAN_An_split & outflags)
799 			outflags |= MMAN_br;
800 		else if (SEC_AUTHORS == n->sec &&
801 		    ! (MMAN_An_nosplit & outflags))
802 			outflags |= MMAN_An_split;
803 		return(1);
804 	}
805 }
806 
807 static int
808 pre_ap(DECL_ARGS)
809 {
810 
811 	outflags &= ~MMAN_spc;
812 	print_word("'");
813 	outflags &= ~MMAN_spc;
814 	return(0);
815 }
816 
817 static int
818 pre_bd(DECL_ARGS)
819 {
820 
821 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
822 
823 	if (DISP_unfilled == n->norm->Bd.type ||
824 	    DISP_literal  == n->norm->Bd.type)
825 		print_line(".nf", 0);
826 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
827 		outflags |= MMAN_sp;
828 	print_offs(n->norm->Bd.offs);
829 	return(1);
830 }
831 
832 static void
833 post_bd(DECL_ARGS)
834 {
835 
836 	/* Close out this display. */
837 	print_line(".RE", MMAN_nl);
838 	if (DISP_unfilled == n->norm->Bd.type ||
839 	    DISP_literal  == n->norm->Bd.type)
840 		print_line(".fi", MMAN_nl);
841 
842 	/* Maybe we are inside an enclosing list? */
843 	if (NULL != n->parent->next)
844 		mid_it();
845 }
846 
847 static int
848 pre_bf(DECL_ARGS)
849 {
850 
851 	switch (n->type) {
852 	case (MDOC_BLOCK):
853 		return(1);
854 	case (MDOC_BODY):
855 		break;
856 	default:
857 		return(0);
858 	}
859 	switch (n->norm->Bf.font) {
860 	case (FONT_Em):
861 		font_push('I');
862 		break;
863 	case (FONT_Sy):
864 		font_push('B');
865 		break;
866 	default:
867 		font_push('R');
868 		break;
869 	}
870 	return(1);
871 }
872 
873 static void
874 post_bf(DECL_ARGS)
875 {
876 
877 	if (MDOC_BODY == n->type)
878 		font_pop();
879 }
880 
881 static int
882 pre_bk(DECL_ARGS)
883 {
884 
885 	switch (n->type) {
886 	case (MDOC_BLOCK):
887 		return(1);
888 	case (MDOC_BODY):
889 		outflags |= MMAN_Bk;
890 		return(1);
891 	default:
892 		return(0);
893 	}
894 }
895 
896 static void
897 post_bk(DECL_ARGS)
898 {
899 
900 	if (MDOC_BODY == n->type && ! (MDOC_SYNPRETTY & n->flags))
901 		outflags &= ~MMAN_Bk;
902 }
903 
904 static int
905 pre_bl(DECL_ARGS)
906 {
907 	size_t		 icol;
908 
909 	/*
910 	 * print_offs() will increase the -offset to account for
911 	 * a possible enclosing .It, but any enclosed .It blocks
912 	 * just nest and do not add up their indentation.
913 	 */
914 	if (n->norm->Bl.offs) {
915 		print_offs(n->norm->Bl.offs);
916 		Bl_stack[Bl_stack_len++] = 0;
917 	}
918 
919 	switch (n->norm->Bl.type) {
920 	case (LIST_enum):
921 		n->norm->Bl.count = 0;
922 		return(1);
923 	case (LIST_column):
924 		break;
925 	default:
926 		return(1);
927 	}
928 
929 	print_line(".TS", MMAN_nl);
930 	for (icol = 0; icol < n->norm->Bl.ncols; icol++)
931 		print_word("l");
932 	print_word(".");
933 	outflags |= MMAN_nl;
934 	return(1);
935 }
936 
937 static void
938 post_bl(DECL_ARGS)
939 {
940 
941 	switch (n->norm->Bl.type) {
942 	case (LIST_column):
943 		print_line(".TE", 0);
944 		break;
945 	case (LIST_enum):
946 		n->norm->Bl.count = 0;
947 		break;
948 	default:
949 		break;
950 	}
951 
952 	if (n->norm->Bl.offs) {
953 		print_line(".RE", MMAN_nl);
954 		assert(Bl_stack_len);
955 		Bl_stack_len--;
956 		assert(0 == Bl_stack[Bl_stack_len]);
957 	} else {
958 		outflags |= MMAN_PP | MMAN_nl;
959 		outflags &= ~(MMAN_sp | MMAN_br);
960 	}
961 
962 	/* Maybe we are inside an enclosing list? */
963 	if (NULL != n->parent->next)
964 		mid_it();
965 
966 }
967 
968 static int
969 pre_br(DECL_ARGS)
970 {
971 
972 	outflags |= MMAN_br;
973 	return(0);
974 }
975 
976 static int
977 pre_bx(DECL_ARGS)
978 {
979 
980 	n = n->child;
981 	if (n) {
982 		print_word(n->string);
983 		outflags &= ~MMAN_spc;
984 		n = n->next;
985 	}
986 	print_word("BSD");
987 	if (NULL == n)
988 		return(0);
989 	outflags &= ~MMAN_spc;
990 	print_word("-");
991 	outflags &= ~MMAN_spc;
992 	print_word(n->string);
993 	return(0);
994 }
995 
996 static int
997 pre_dl(DECL_ARGS)
998 {
999 
1000 	print_offs("6n");
1001 	return(1);
1002 }
1003 
1004 static void
1005 post_dl(DECL_ARGS)
1006 {
1007 
1008 	print_line(".RE", MMAN_nl);
1009 
1010 	/* Maybe we are inside an enclosing list? */
1011 	if (NULL != n->parent->next)
1012 		mid_it();
1013 }
1014 
1015 static int
1016 pre_em(DECL_ARGS)
1017 {
1018 
1019 	font_push('I');
1020 	return(1);
1021 }
1022 
1023 static void
1024 post_eo(DECL_ARGS)
1025 {
1026 
1027 	if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
1028 		outflags &= ~MMAN_spc;
1029 }
1030 
1031 static int
1032 pre_fa(DECL_ARGS)
1033 {
1034 
1035 	if (MDOC_Fa == n->tok)
1036 		n = n->child;
1037 
1038 	while (NULL != n) {
1039 		font_push('I');
1040 		print_node(meta, n);
1041 		font_pop();
1042 		if (NULL != (n = n->next))
1043 			print_word(",");
1044 	}
1045 	return(0);
1046 }
1047 
1048 static void
1049 post_fa(DECL_ARGS)
1050 {
1051 
1052 	if (NULL != n->next && MDOC_Fa == n->next->tok)
1053 		print_word(",");
1054 }
1055 
1056 static int
1057 pre_fd(DECL_ARGS)
1058 {
1059 
1060 	pre_syn(n);
1061 	font_push('B');
1062 	return(1);
1063 }
1064 
1065 static void
1066 post_fd(DECL_ARGS)
1067 {
1068 
1069 	font_pop();
1070 	outflags |= MMAN_br;
1071 }
1072 
1073 static int
1074 pre_fl(DECL_ARGS)
1075 {
1076 
1077 	font_push('B');
1078 	print_word("\\-");
1079 	outflags &= ~MMAN_spc;
1080 	return(1);
1081 }
1082 
1083 static void
1084 post_fl(DECL_ARGS)
1085 {
1086 
1087 	font_pop();
1088 	if (0 == n->nchild && NULL != n->next &&
1089 			n->next->line == n->line)
1090 		outflags &= ~MMAN_spc;
1091 }
1092 
1093 static int
1094 pre_fn(DECL_ARGS)
1095 {
1096 
1097 	pre_syn(n);
1098 
1099 	n = n->child;
1100 	if (NULL == n)
1101 		return(0);
1102 
1103 	font_push('B');
1104 	print_node(meta, n);
1105 	font_pop();
1106 	outflags &= ~MMAN_spc;
1107 	print_word("(");
1108 	outflags &= ~MMAN_spc;
1109 
1110 	n = n->next;
1111 	if (NULL != n)
1112 		pre_fa(meta, n);
1113 	return(0);
1114 }
1115 
1116 static void
1117 post_fn(DECL_ARGS)
1118 {
1119 
1120 	print_word(")");
1121 	if (MDOC_SYNPRETTY & n->flags) {
1122 		print_word(";");
1123 		outflags |= MMAN_br;
1124 	}
1125 }
1126 
1127 static int
1128 pre_fo(DECL_ARGS)
1129 {
1130 
1131 	switch (n->type) {
1132 	case (MDOC_BLOCK):
1133 		pre_syn(n);
1134 		break;
1135 	case (MDOC_HEAD):
1136 		font_push('B');
1137 		break;
1138 	case (MDOC_BODY):
1139 		outflags &= ~MMAN_spc;
1140 		print_word("(");
1141 		outflags &= ~MMAN_spc;
1142 		break;
1143 	default:
1144 		break;
1145 	}
1146 	return(1);
1147 }
1148 
1149 static void
1150 post_fo(DECL_ARGS)
1151 {
1152 
1153 	switch (n->type) {
1154 	case (MDOC_HEAD):
1155 		font_pop();
1156 		break;
1157 	case (MDOC_BODY):
1158 		post_fn(meta, n);
1159 		break;
1160 	default:
1161 		break;
1162 	}
1163 }
1164 
1165 static int
1166 pre_ft(DECL_ARGS)
1167 {
1168 
1169 	pre_syn(n);
1170 	font_push('I');
1171 	return(1);
1172 }
1173 
1174 static int
1175 pre_in(DECL_ARGS)
1176 {
1177 
1178 	if (MDOC_SYNPRETTY & n->flags) {
1179 		pre_syn(n);
1180 		font_push('B');
1181 		print_word("#include <");
1182 		outflags &= ~MMAN_spc;
1183 	} else {
1184 		print_word("<");
1185 		outflags &= ~MMAN_spc;
1186 		font_push('I');
1187 	}
1188 	return(1);
1189 }
1190 
1191 static void
1192 post_in(DECL_ARGS)
1193 {
1194 
1195 	if (MDOC_SYNPRETTY & n->flags) {
1196 		outflags &= ~MMAN_spc;
1197 		print_word(">");
1198 		font_pop();
1199 		outflags |= MMAN_br;
1200 	} else {
1201 		font_pop();
1202 		outflags &= ~MMAN_spc;
1203 		print_word(">");
1204 	}
1205 }
1206 
1207 static int
1208 pre_it(DECL_ARGS)
1209 {
1210 	const struct mdoc_node *bln;
1211 
1212 	switch (n->type) {
1213 	case (MDOC_HEAD):
1214 		outflags |= MMAN_PP | MMAN_nl;
1215 		bln = n->parent->parent;
1216 		if (0 == bln->norm->Bl.comp ||
1217 		    (NULL == n->parent->prev &&
1218 		     NULL == bln->parent->prev))
1219 			outflags |= MMAN_sp;
1220 		outflags &= ~MMAN_br;
1221 		switch (bln->norm->Bl.type) {
1222 		case (LIST_item):
1223 			return(0);
1224 		case (LIST_inset):
1225 			/* FALLTHROUGH */
1226 		case (LIST_diag):
1227 			/* FALLTHROUGH */
1228 		case (LIST_ohang):
1229 			if (bln->norm->Bl.type == LIST_diag)
1230 				print_line(".B \"", 0);
1231 			else
1232 				print_line(".R \"", 0);
1233 			outflags &= ~MMAN_spc;
1234 			return(1);
1235 		case (LIST_bullet):
1236 			/* FALLTHROUGH */
1237 		case (LIST_dash):
1238 			/* FALLTHROUGH */
1239 		case (LIST_hyphen):
1240 			print_width(bln->norm->Bl.width, NULL, 0);
1241 			TPremain = 0;
1242 			outflags |= MMAN_nl;
1243 			font_push('B');
1244 			if (LIST_bullet == bln->norm->Bl.type)
1245 				print_word("o");
1246 			else
1247 				print_word("-");
1248 			font_pop();
1249 			break;
1250 		case (LIST_enum):
1251 			print_width(bln->norm->Bl.width, NULL, 0);
1252 			TPremain = 0;
1253 			outflags |= MMAN_nl;
1254 			print_count(&bln->norm->Bl.count);
1255 			break;
1256 		case (LIST_hang):
1257 			print_width(bln->norm->Bl.width, n->child, 6);
1258 			TPremain = 0;
1259 			break;
1260 		case (LIST_tag):
1261 			print_width(bln->norm->Bl.width, n->child, 0);
1262 			putchar('\n');
1263 			outflags &= ~MMAN_spc;
1264 			return(1);
1265 		default:
1266 			return(1);
1267 		}
1268 		outflags |= MMAN_nl;
1269 	default:
1270 		break;
1271 	}
1272 	return(1);
1273 }
1274 
1275 /*
1276  * This function is called after closing out an indented block.
1277  * If we are inside an enclosing list, restore its indentation.
1278  */
1279 static void
1280 mid_it(void)
1281 {
1282 	char		 buf[24];
1283 
1284 	/* Nothing to do outside a list. */
1285 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1286 		return;
1287 
1288 	/* The indentation has already been set up. */
1289 	if (Bl_stack_post[Bl_stack_len - 1])
1290 		return;
1291 
1292 	/* Restore the indentation of the enclosing list. */
1293 	print_line(".RS", MMAN_Bk_susp);
1294 	snprintf(buf, sizeof(buf), "%ldn", Bl_stack[Bl_stack_len - 1]);
1295 	print_word(buf);
1296 
1297 	/* Remeber to close out this .RS block later. */
1298 	Bl_stack_post[Bl_stack_len - 1] = 1;
1299 }
1300 
1301 static void
1302 post_it(DECL_ARGS)
1303 {
1304 	const struct mdoc_node *bln;
1305 
1306 	bln = n->parent->parent;
1307 
1308 	switch (n->type) {
1309 	case (MDOC_HEAD):
1310 		switch (bln->norm->Bl.type) {
1311 		case (LIST_diag):
1312 			outflags &= ~MMAN_spc;
1313 			print_word("\\ ");
1314 			break;
1315 		case (LIST_ohang):
1316 			outflags |= MMAN_br;
1317 			break;
1318 		default:
1319 			break;
1320 		}
1321 		break;
1322 	case (MDOC_BODY):
1323 		switch (bln->norm->Bl.type) {
1324 		case (LIST_bullet):
1325 			/* FALLTHROUGH */
1326 		case (LIST_dash):
1327 			/* FALLTHROUGH */
1328 		case (LIST_hyphen):
1329 			/* FALLTHROUGH */
1330 		case (LIST_enum):
1331 			/* FALLTHROUGH */
1332 		case (LIST_hang):
1333 			/* FALLTHROUGH */
1334 		case (LIST_tag):
1335 			assert(Bl_stack_len);
1336 			Bl_stack[--Bl_stack_len] = 0;
1337 
1338 			/*
1339 			 * Our indentation had to be restored
1340 			 * after a child display or child list.
1341 			 * Close out that indentation block now.
1342 			 */
1343 			if (Bl_stack_post[Bl_stack_len]) {
1344 				print_line(".RE", MMAN_nl);
1345 				Bl_stack_post[Bl_stack_len] = 0;
1346 			}
1347 			break;
1348 		case (LIST_column):
1349 			if (NULL != n->next) {
1350 				putchar('\t');
1351 				outflags &= ~MMAN_spc;
1352 			}
1353 			break;
1354 		default:
1355 			break;
1356 		}
1357 		break;
1358 	default:
1359 		break;
1360 	}
1361 }
1362 
1363 static void
1364 post_lb(DECL_ARGS)
1365 {
1366 
1367 	if (SEC_LIBRARY == n->sec)
1368 		outflags |= MMAN_br;
1369 }
1370 
1371 static int
1372 pre_lk(DECL_ARGS)
1373 {
1374 	const struct mdoc_node *link, *descr;
1375 
1376 	if (NULL == (link = n->child))
1377 		return(0);
1378 
1379 	if (NULL != (descr = link->next)) {
1380 		font_push('I');
1381 		while (NULL != descr) {
1382 			print_word(descr->string);
1383 			descr = descr->next;
1384 		}
1385 		print_word(":");
1386 		font_pop();
1387 	}
1388 
1389 	font_push('B');
1390 	print_word(link->string);
1391 	font_pop();
1392 	return(0);
1393 }
1394 
1395 static int
1396 pre_li(DECL_ARGS)
1397 {
1398 
1399 	font_push('R');
1400 	return(1);
1401 }
1402 
1403 static int
1404 pre_nm(DECL_ARGS)
1405 {
1406 	char	*name;
1407 
1408 	if (MDOC_BLOCK == n->type)
1409 		pre_syn(n);
1410 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1411 		return(1);
1412 	name = n->child ? n->child->string : meta->name;
1413 	if (NULL == name)
1414 		return(0);
1415 	if (MDOC_HEAD == n->type) {
1416 		if (NULL == n->parent->prev)
1417 			outflags |= MMAN_sp;
1418 		print_block(".HP", 0);
1419 		printf(" %ldn", strlen(name) + 1);
1420 		outflags |= MMAN_nl;
1421 	}
1422 	font_push('B');
1423 	if (NULL == n->child)
1424 		print_word(meta->name);
1425 	return(1);
1426 }
1427 
1428 static void
1429 post_nm(DECL_ARGS)
1430 {
1431 
1432 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
1433 		return;
1434 	font_pop();
1435 }
1436 
1437 static int
1438 pre_no(DECL_ARGS)
1439 {
1440 
1441 	outflags |= MMAN_spc_force;
1442 	return(1);
1443 }
1444 
1445 static int
1446 pre_ns(DECL_ARGS)
1447 {
1448 
1449 	outflags &= ~MMAN_spc;
1450 	return(0);
1451 }
1452 
1453 static void
1454 post_pf(DECL_ARGS)
1455 {
1456 
1457 	outflags &= ~MMAN_spc;
1458 }
1459 
1460 static int
1461 pre_pp(DECL_ARGS)
1462 {
1463 
1464 	if (MDOC_It != n->parent->tok)
1465 		outflags |= MMAN_PP;
1466 	outflags |= MMAN_sp | MMAN_nl;
1467 	outflags &= ~MMAN_br;
1468 	return(0);
1469 }
1470 
1471 static int
1472 pre_rs(DECL_ARGS)
1473 {
1474 
1475 	if (SEC_SEE_ALSO == n->sec) {
1476 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1477 		outflags &= ~MMAN_br;
1478 	}
1479 	return(1);
1480 }
1481 
1482 static int
1483 pre_sm(DECL_ARGS)
1484 {
1485 
1486 	assert(n->child && MDOC_TEXT == n->child->type);
1487 	if (0 == strcmp("on", n->child->string))
1488 		outflags |= MMAN_Sm | MMAN_spc;
1489 	else
1490 		outflags &= ~MMAN_Sm;
1491 	return(0);
1492 }
1493 
1494 static int
1495 pre_sp(DECL_ARGS)
1496 {
1497 
1498 	if (MMAN_PP & outflags) {
1499 		outflags &= ~MMAN_PP;
1500 		print_line(".PP", 0);
1501 	} else
1502 		print_line(".sp", 0);
1503 	return(1);
1504 }
1505 
1506 static void
1507 post_sp(DECL_ARGS)
1508 {
1509 
1510 	outflags |= MMAN_nl;
1511 }
1512 
1513 static int
1514 pre_sy(DECL_ARGS)
1515 {
1516 
1517 	font_push('B');
1518 	return(1);
1519 }
1520 
1521 static int
1522 pre_vt(DECL_ARGS)
1523 {
1524 
1525 	if (MDOC_SYNPRETTY & n->flags) {
1526 		switch (n->type) {
1527 		case (MDOC_BLOCK):
1528 			pre_syn(n);
1529 			return(1);
1530 		case (MDOC_BODY):
1531 			break;
1532 		default:
1533 			return(0);
1534 		}
1535 	}
1536 	font_push('I');
1537 	return(1);
1538 }
1539 
1540 static void
1541 post_vt(DECL_ARGS)
1542 {
1543 
1544 	if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
1545 		return;
1546 	font_pop();
1547 }
1548 
1549 static int
1550 pre_xr(DECL_ARGS)
1551 {
1552 
1553 	n = n->child;
1554 	if (NULL == n)
1555 		return(0);
1556 	print_node(meta, n);
1557 	n = n->next;
1558 	if (NULL == n)
1559 		return(0);
1560 	outflags &= ~MMAN_spc;
1561 	print_word("(");
1562 	print_node(meta, n);
1563 	print_word(")");
1564 	return(0);
1565 }
1566 
1567 static int
1568 pre_ux(DECL_ARGS)
1569 {
1570 
1571 	print_word(manacts[n->tok].prefix);
1572 	if (NULL == n->child)
1573 		return(0);
1574 	outflags &= ~MMAN_spc;
1575 	print_word("\\ ");
1576 	outflags &= ~MMAN_spc;
1577 	return(1);
1578 }
1579