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