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