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