Home | History | Annotate | Line # | Download | only in dist
      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 (at) 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 
     36 typedef	int	(*int_fp)(DECL_ARGS);
     37 typedef	void	(*void_fp)(DECL_ARGS);
     38 
     39 struct	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 
     47 static	int	  cond_body(DECL_ARGS);
     48 static	int	  cond_head(DECL_ARGS);
     49 static  void	  font_push(char);
     50 static	void	  font_pop(void);
     51 static	int	  man_strlen(const char *);
     52 static	void	  mid_it(void);
     53 static	void	  post__t(DECL_ARGS);
     54 static	void	  post_aq(DECL_ARGS);
     55 static	void	  post_bd(DECL_ARGS);
     56 static	void	  post_bf(DECL_ARGS);
     57 static	void	  post_bk(DECL_ARGS);
     58 static	void	  post_bl(DECL_ARGS);
     59 static	void	  post_dl(DECL_ARGS);
     60 static	void	  post_en(DECL_ARGS);
     61 static	void	  post_enc(DECL_ARGS);
     62 static	void	  post_eo(DECL_ARGS);
     63 static	void	  post_fa(DECL_ARGS);
     64 static	void	  post_fd(DECL_ARGS);
     65 static	void	  post_fl(DECL_ARGS);
     66 static	void	  post_fn(DECL_ARGS);
     67 static	void	  post_fo(DECL_ARGS);
     68 static	void	  post_font(DECL_ARGS);
     69 static	void	  post_in(DECL_ARGS);
     70 static	void	  post_it(DECL_ARGS);
     71 static	void	  post_lb(DECL_ARGS);
     72 static	void	  post_nm(DECL_ARGS);
     73 static	void	  post_percent(DECL_ARGS);
     74 static	void	  post_pf(DECL_ARGS);
     75 static	void	  post_sect(DECL_ARGS);
     76 static	void	  post_vt(DECL_ARGS);
     77 static	int	  pre__t(DECL_ARGS);
     78 static	int	  pre_abort(DECL_ARGS);
     79 static	int	  pre_an(DECL_ARGS);
     80 static	int	  pre_ap(DECL_ARGS);
     81 static	int	  pre_aq(DECL_ARGS);
     82 static	int	  pre_bd(DECL_ARGS);
     83 static	int	  pre_bf(DECL_ARGS);
     84 static	int	  pre_bk(DECL_ARGS);
     85 static	int	  pre_bl(DECL_ARGS);
     86 static	void	  pre_br(DECL_ARGS);
     87 static	int	  pre_dl(DECL_ARGS);
     88 static	int	  pre_en(DECL_ARGS);
     89 static	int	  pre_enc(DECL_ARGS);
     90 static	int	  pre_em(DECL_ARGS);
     91 static	int	  pre_skip(DECL_ARGS);
     92 static	int	  pre_eo(DECL_ARGS);
     93 static	int	  pre_ex(DECL_ARGS);
     94 static	int	  pre_fa(DECL_ARGS);
     95 static	int	  pre_fd(DECL_ARGS);
     96 static	int	  pre_fl(DECL_ARGS);
     97 static	int	  pre_fn(DECL_ARGS);
     98 static	int	  pre_fo(DECL_ARGS);
     99 static	void	  pre_ft(DECL_ARGS);
    100 static	int	  pre_Ft(DECL_ARGS);
    101 static	int	  pre_in(DECL_ARGS);
    102 static	int	  pre_it(DECL_ARGS);
    103 static	int	  pre_lk(DECL_ARGS);
    104 static	int	  pre_li(DECL_ARGS);
    105 static	int	  pre_nm(DECL_ARGS);
    106 static	int	  pre_no(DECL_ARGS);
    107 static	void	  pre_noarg(DECL_ARGS);
    108 static	int	  pre_ns(DECL_ARGS);
    109 static	void	  pre_onearg(DECL_ARGS);
    110 static	int	  pre_pp(DECL_ARGS);
    111 static	int	  pre_rs(DECL_ARGS);
    112 static	int	  pre_sm(DECL_ARGS);
    113 static	void	  pre_sp(DECL_ARGS);
    114 static	int	  pre_sect(DECL_ARGS);
    115 static	int	  pre_sy(DECL_ARGS);
    116 static	void	  pre_syn(struct roff_node *);
    117 static	void	  pre_ta(DECL_ARGS);
    118 static	int	  pre_vt(DECL_ARGS);
    119 static	int	  pre_xr(DECL_ARGS);
    120 static	void	  print_word(const char *);
    121 static	void	  print_line(const char *, int);
    122 static	void	  print_block(const char *, int);
    123 static	void	  print_offs(const char *, int);
    124 static	void	  print_width(const struct mdoc_bl *,
    125 			const struct roff_node *);
    126 static	void	  print_count(int *);
    127 static	void	  print_node(DECL_ARGS);
    128 
    129 static 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 
    144 static 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 };
    267 static const struct mdoc_man_act *mdoc_man_act(enum roff_tok);
    268 
    269 static	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 
    286 static	int		Bl_stack[BL_STACK_MAX];  /* offsets [chars] */
    287 static	int		Bl_stack_post[BL_STACK_MAX];  /* add final .RE */
    288 static	int		Bl_stack_len;  /* number of nested Bl blocks */
    289 static	int		TPremain;  /* characters before tag is full */
    290 
    291 static	struct {
    292 	char	*head;
    293 	char	*tail;
    294 	size_t	 size;
    295 }	fontqueue;
    296 
    297 
    298 static const struct mdoc_man_act *
    299 mdoc_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 
    305 static int
    306 man_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 
    349 static void
    350 font_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 
    365 static void
    366 font_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 
    377 static void
    378 print_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 
    462 static void
    463 print_line(const char *s, int newflags)
    464 {
    465 
    466 	outflags |= MMAN_nl;
    467 	print_word(s);
    468 	outflags |= newflags;
    469 }
    470 
    471 static void
    472 print_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 
    489 static void
    490 print_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  */
    541 static void
    542 print_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 
    599 static void
    600 print_count(int *count)
    601 {
    602 	char		  buf[24];
    603 
    604 	(void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
    605 	print_word(buf);
    606 }
    607 
    608 void
    609 man_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 
    639 static void
    640 print_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 
    732 static int
    733 cond_head(DECL_ARGS)
    734 {
    735 
    736 	return n->type == ROFFT_HEAD;
    737 }
    738 
    739 static int
    740 cond_body(DECL_ARGS)
    741 {
    742 
    743 	return n->type == ROFFT_BODY;
    744 }
    745 
    746 static int
    747 pre_abort(DECL_ARGS)
    748 {
    749 	abort();
    750 }
    751 
    752 static int
    753 pre_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 
    765 static void
    766 post_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 
    777 static int
    778 pre_ex(DECL_ARGS)
    779 {
    780 	outflags |= MMAN_br | MMAN_nl;
    781 	return 1;
    782 }
    783 
    784 static void
    785 post_font(DECL_ARGS)
    786 {
    787 
    788 	font_pop();
    789 }
    790 
    791 static void
    792 post_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 
    815 static int
    816 pre__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 
    827 static void
    828 post__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  */
    842 static int
    843 pre_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  */
    859 static void
    860 post_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. */
    874 static void
    875 pre_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 
    911 static int
    912 pre_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 
    934 static int
    935 pre_ap(DECL_ARGS)
    936 {
    937 
    938 	outflags &= ~MMAN_spc;
    939 	print_word("'");
    940 	outflags &= ~MMAN_spc;
    941 	return 0;
    942 }
    943 
    944 static int
    945 pre_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 
    954 static void
    955 post_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 
    963 static int
    964 pre_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 
    976 static void
    977 post_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 
   1006 static int
   1007 pre_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 
   1032 static void
   1033 post_bf(DECL_ARGS)
   1034 {
   1035 
   1036 	if (n->type == ROFFT_BODY)
   1037 		font_pop();
   1038 }
   1039 
   1040 static int
   1041 pre_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 
   1055 static void
   1056 post_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 
   1072 static int
   1073 pre_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 
   1107 static void
   1108 post_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 
   1138 static void
   1139 pre_br(DECL_ARGS)
   1140 {
   1141 	outflags |= MMAN_br;
   1142 }
   1143 
   1144 static int
   1145 pre_dl(DECL_ARGS)
   1146 {
   1147 	print_offs("6n", 0);
   1148 	return 1;
   1149 }
   1150 
   1151 static void
   1152 post_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 
   1161 static int
   1162 pre_em(DECL_ARGS)
   1163 {
   1164 
   1165 	font_push('I');
   1166 	return 1;
   1167 }
   1168 
   1169 static int
   1170 pre_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 
   1182 static void
   1183 post_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 
   1196 static int
   1197 pre_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 
   1212 static void
   1213 post_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 
   1233 static int
   1234 pre_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 
   1255 static void
   1256 post_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 
   1264 static int
   1265 pre_fd(DECL_ARGS)
   1266 {
   1267 	pre_syn(n);
   1268 	font_push('B');
   1269 	return 1;
   1270 }
   1271 
   1272 static void
   1273 post_fd(DECL_ARGS)
   1274 {
   1275 	font_pop();
   1276 	outflags |= MMAN_br;
   1277 }
   1278 
   1279 static int
   1280 pre_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 
   1289 static void
   1290 post_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 
   1302 static int
   1303 pre_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 
   1328 static void
   1329 post_fn(DECL_ARGS)
   1330 {
   1331 
   1332 	print_word(")");
   1333 	if (NODE_SYNPRETTY & n->flags) {
   1334 		print_word(";");
   1335 		outflags |= MMAN_PP;
   1336 	}
   1337 }
   1338 
   1339 static int
   1340 pre_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 
   1365 static void
   1366 post_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 
   1382 static int
   1383 pre_Ft(DECL_ARGS)
   1384 {
   1385 
   1386 	pre_syn(n);
   1387 	font_push('I');
   1388 	return 1;
   1389 }
   1390 
   1391 static void
   1392 pre_ft(DECL_ARGS)
   1393 {
   1394 	print_line(".ft", 0);
   1395 	print_word(n->child->string);
   1396 	outflags |= MMAN_nl;
   1397 }
   1398 
   1399 static int
   1400 pre_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 
   1416 static void
   1417 post_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 
   1432 static int
   1433 pre_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  */
   1502 static void
   1503 mid_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 
   1525 static void
   1526 post_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 
   1582 static void
   1583 post_lb(DECL_ARGS)
   1584 {
   1585 
   1586 	if (SEC_LIBRARY == n->sec)
   1587 		outflags |= MMAN_br;
   1588 }
   1589 
   1590 static int
   1591 pre_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 
   1628 static void
   1629 pre_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 
   1643 static int
   1644 pre_li(DECL_ARGS)
   1645 {
   1646 	font_push('R');
   1647 	return 1;
   1648 }
   1649 
   1650 static int
   1651 pre_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 
   1680 static void
   1681 post_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 
   1697 static int
   1698 pre_no(DECL_ARGS)
   1699 {
   1700 	outflags |= MMAN_spc_force;
   1701 	return 1;
   1702 }
   1703 
   1704 static void
   1705 pre_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 
   1714 static int
   1715 pre_ns(DECL_ARGS)
   1716 {
   1717 	outflags &= ~MMAN_spc;
   1718 	return 0;
   1719 }
   1720 
   1721 static void
   1722 post_pf(DECL_ARGS)
   1723 {
   1724 
   1725 	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
   1726 		outflags &= ~MMAN_spc;
   1727 }
   1728 
   1729 static int
   1730 pre_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 
   1740 static int
   1741 pre_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 
   1751 static int
   1752 pre_skip(DECL_ARGS)
   1753 {
   1754 
   1755 	return 0;
   1756 }
   1757 
   1758 static int
   1759 pre_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 
   1775 static void
   1776 pre_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 
   1789 static int
   1790 pre_sy(DECL_ARGS)
   1791 {
   1792 
   1793 	font_push('B');
   1794 	return 1;
   1795 }
   1796 
   1797 static void
   1798 pre_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 
   1806 static int
   1807 pre_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 
   1825 static void
   1826 post_vt(DECL_ARGS)
   1827 {
   1828 
   1829 	if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
   1830 		return;
   1831 	font_pop();
   1832 }
   1833 
   1834 static int
   1835 pre_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