Home | History | Annotate | Line # | Download | only in dist
man_term.c revision 1.11
      1 /*	$Id: man_term.c,v 1.11 2015/12/17 22:31:12 christos Exp $ */
      2 /*
      3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps (at) bsd.lv>
      4  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze (at) openbsd.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 #include "config.h"
     19 
     20 #include <sys/types.h>
     21 
     22 #include <assert.h>
     23 #include <ctype.h>
     24 #include <limits.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 
     29 #include "mandoc.h"
     30 #include "mandoc_aux.h"
     31 #include "out.h"
     32 #include "man.h"
     33 #include "term.h"
     34 #include "main.h"
     35 
     36 #define	MAXMARGINS	  64 /* maximum number of indented scopes */
     37 
     38 struct	mtermp {
     39 	int		  fl;
     40 #define	MANT_LITERAL	 (1 << 0)
     41 	int		  lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
     42 	int		  lmargincur; /* index of current margin */
     43 	int		  lmarginsz; /* actual number of nested margins */
     44 	size_t		  offset; /* default offset to visible page */
     45 	int		  pardist; /* vert. space before par., unit: [v] */
     46 };
     47 
     48 #define	DECL_ARGS	  struct termp *p, \
     49 			  struct mtermp *mt, \
     50 			  struct man_node *n, \
     51 			  const struct man_meta *meta
     52 
     53 struct	termact {
     54 	int		(*pre)(DECL_ARGS);
     55 	void		(*post)(DECL_ARGS);
     56 	int		  flags;
     57 #define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
     58 };
     59 
     60 static	void		  print_man_nodelist(DECL_ARGS);
     61 static	void		  print_man_node(DECL_ARGS);
     62 static	void		  print_man_head(struct termp *, const void *);
     63 static	void		  print_man_foot(struct termp *, const void *);
     64 static	void		  print_bvspace(struct termp *,
     65 				const struct man_node *, int);
     66 
     67 static	int		  pre_B(DECL_ARGS);
     68 static	int		  pre_HP(DECL_ARGS);
     69 static	int		  pre_I(DECL_ARGS);
     70 static	int		  pre_IP(DECL_ARGS);
     71 static	int		  pre_OP(DECL_ARGS);
     72 static	int		  pre_PD(DECL_ARGS);
     73 static	int		  pre_PP(DECL_ARGS);
     74 static	int		  pre_RS(DECL_ARGS);
     75 static	int		  pre_SH(DECL_ARGS);
     76 static	int		  pre_SS(DECL_ARGS);
     77 static	int		  pre_TP(DECL_ARGS);
     78 static	int		  pre_UR(DECL_ARGS);
     79 static	int		  pre_alternate(DECL_ARGS);
     80 static	int		  pre_ft(DECL_ARGS);
     81 static	int		  pre_ign(DECL_ARGS);
     82 static	int		  pre_in(DECL_ARGS);
     83 static	int		  pre_literal(DECL_ARGS);
     84 static	int		  pre_ll(DECL_ARGS);
     85 static	int		  pre_sp(DECL_ARGS);
     86 
     87 static	void		  post_IP(DECL_ARGS);
     88 static	void		  post_HP(DECL_ARGS);
     89 static	void		  post_RS(DECL_ARGS);
     90 static	void		  post_SH(DECL_ARGS);
     91 static	void		  post_SS(DECL_ARGS);
     92 static	void		  post_TP(DECL_ARGS);
     93 static	void		  post_UR(DECL_ARGS);
     94 
     95 static	const struct termact termacts[MAN_MAX] = {
     96 	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
     97 	{ NULL, NULL, 0 }, /* TH */
     98 	{ pre_SH, post_SH, 0 }, /* SH */
     99 	{ pre_SS, post_SS, 0 }, /* SS */
    100 	{ pre_TP, post_TP, 0 }, /* TP */
    101 	{ pre_PP, NULL, 0 }, /* LP */
    102 	{ pre_PP, NULL, 0 }, /* PP */
    103 	{ pre_PP, NULL, 0 }, /* P */
    104 	{ pre_IP, post_IP, 0 }, /* IP */
    105 	{ pre_HP, post_HP, 0 }, /* HP */
    106 	{ NULL, NULL, 0 }, /* SM */
    107 	{ pre_B, NULL, 0 }, /* SB */
    108 	{ pre_alternate, NULL, 0 }, /* BI */
    109 	{ pre_alternate, NULL, 0 }, /* IB */
    110 	{ pre_alternate, NULL, 0 }, /* BR */
    111 	{ pre_alternate, NULL, 0 }, /* RB */
    112 	{ NULL, NULL, 0 }, /* R */
    113 	{ pre_B, NULL, 0 }, /* B */
    114 	{ pre_I, NULL, 0 }, /* I */
    115 	{ pre_alternate, NULL, 0 }, /* IR */
    116 	{ pre_alternate, NULL, 0 }, /* RI */
    117 	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
    118 	{ pre_literal, NULL, 0 }, /* nf */
    119 	{ pre_literal, NULL, 0 }, /* fi */
    120 	{ NULL, NULL, 0 }, /* RE */
    121 	{ pre_RS, post_RS, 0 }, /* RS */
    122 	{ pre_ign, NULL, 0 }, /* DT */
    123 	{ pre_ign, NULL, MAN_NOTEXT }, /* UC */
    124 	{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
    125 	{ pre_ign, NULL, 0 }, /* AT */
    126 	{ pre_in, NULL, MAN_NOTEXT }, /* in */
    127 	{ pre_ft, NULL, MAN_NOTEXT }, /* ft */
    128 	{ pre_OP, NULL, 0 }, /* OP */
    129 	{ pre_literal, NULL, 0 }, /* EX */
    130 	{ pre_literal, NULL, 0 }, /* EE */
    131 	{ pre_UR, post_UR, 0 }, /* UR */
    132 	{ NULL, NULL, 0 }, /* UE */
    133 	{ pre_ll, NULL, MAN_NOTEXT }, /* ll */
    134 };
    135 
    136 
    137 void
    138 terminal_man(void *arg, const struct man *man)
    139 {
    140 	struct termp		*p;
    141 	const struct man_meta	*meta;
    142 	struct man_node		*n;
    143 	struct mtermp		 mt;
    144 
    145 	p = (struct termp *)arg;
    146 
    147 	p->overstep = 0;
    148 	p->rmargin = p->maxrmargin = p->defrmargin;
    149 	p->tabwidth = term_len(p, 5);
    150 
    151 	n = man_node(man)->child;
    152 	meta = man_meta(man);
    153 
    154 	memset(&mt, 0, sizeof(struct mtermp));
    155 
    156 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
    157 	mt.offset = term_len(p, p->defindent);
    158 	mt.pardist = 1;
    159 
    160 	if (p->synopsisonly) {
    161 		while (n != NULL) {
    162 			if (n->tok == MAN_SH &&
    163 			    n->child->child->type == MAN_TEXT &&
    164 			    !strcmp(n->child->child->string, "SYNOPSIS")) {
    165 				if (n->child->next->child != NULL)
    166 					print_man_nodelist(p, &mt,
    167 					    n->child->next->child, meta);
    168 				term_newln(p);
    169 				break;
    170 			}
    171 			n = n->next;
    172 		}
    173 	} else {
    174 		if (p->defindent == 0)
    175 			p->defindent = 7;
    176 		term_begin(p, print_man_head, print_man_foot, meta);
    177 		p->flags |= TERMP_NOSPACE;
    178 		if (n != NULL)
    179 			print_man_nodelist(p, &mt, n, meta);
    180 		term_end(p);
    181 	}
    182 }
    183 
    184 /*
    185  * Printing leading vertical space before a block.
    186  * This is used for the paragraph macros.
    187  * The rules are pretty simple, since there's very little nesting going
    188  * on here.  Basically, if we're the first within another block (SS/SH),
    189  * then don't emit vertical space.  If we are (RS), then do.  If not the
    190  * first, print it.
    191  */
    192 static void
    193 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
    194 {
    195 	int	 i;
    196 
    197 	term_newln(p);
    198 
    199 	if (n->body && n->body->child)
    200 		if (MAN_TBL == n->body->child->type)
    201 			return;
    202 
    203 	if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
    204 		if (NULL == n->prev)
    205 			return;
    206 
    207 	for (i = 0; i < pardist; i++)
    208 		term_vspace(p);
    209 }
    210 
    211 
    212 static int
    213 pre_ign(DECL_ARGS)
    214 {
    215 
    216 	return(0);
    217 }
    218 
    219 static int
    220 pre_ll(DECL_ARGS)
    221 {
    222 
    223 	term_setwidth(p, n->nchild ? n->child->string : NULL);
    224 	return(0);
    225 }
    226 
    227 static int
    228 pre_I(DECL_ARGS)
    229 {
    230 
    231 	term_fontrepl(p, TERMFONT_UNDER);
    232 	return(1);
    233 }
    234 
    235 static int
    236 pre_literal(DECL_ARGS)
    237 {
    238 
    239 	term_newln(p);
    240 
    241 	if (MAN_nf == n->tok || MAN_EX == n->tok)
    242 		mt->fl |= MANT_LITERAL;
    243 	else
    244 		mt->fl &= ~MANT_LITERAL;
    245 
    246 	/*
    247 	 * Unlike .IP and .TP, .HP does not have a HEAD.
    248 	 * So in case a second call to term_flushln() is needed,
    249 	 * indentation has to be set up explicitly.
    250 	 */
    251 	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
    252 		p->offset = p->rmargin;
    253 		p->rmargin = p->maxrmargin;
    254 		p->trailspace = 0;
    255 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
    256 		p->flags |= TERMP_NOSPACE;
    257 	}
    258 
    259 	return(0);
    260 }
    261 
    262 static int
    263 pre_PD(DECL_ARGS)
    264 {
    265 	struct roffsu	 su;
    266 
    267 	n = n->child;
    268 	if (n == NULL) {
    269 		mt->pardist = 1;
    270 		return(0);
    271 	}
    272 	assert(MAN_TEXT == n->type);
    273 	if (a2roffsu(n->string, &su, SCALE_VS))
    274 		mt->pardist = term_vspan(p, &su);
    275 	return(0);
    276 }
    277 
    278 static int
    279 pre_alternate(DECL_ARGS)
    280 {
    281 	enum termfont		 font[2];
    282 	struct man_node		*nn;
    283 	int			 savelit, i;
    284 
    285 	switch (n->tok) {
    286 	case MAN_RB:
    287 		font[0] = TERMFONT_NONE;
    288 		font[1] = TERMFONT_BOLD;
    289 		break;
    290 	case MAN_RI:
    291 		font[0] = TERMFONT_NONE;
    292 		font[1] = TERMFONT_UNDER;
    293 		break;
    294 	case MAN_BR:
    295 		font[0] = TERMFONT_BOLD;
    296 		font[1] = TERMFONT_NONE;
    297 		break;
    298 	case MAN_BI:
    299 		font[0] = TERMFONT_BOLD;
    300 		font[1] = TERMFONT_UNDER;
    301 		break;
    302 	case MAN_IR:
    303 		font[0] = TERMFONT_UNDER;
    304 		font[1] = TERMFONT_NONE;
    305 		break;
    306 	case MAN_IB:
    307 		font[0] = TERMFONT_UNDER;
    308 		font[1] = TERMFONT_BOLD;
    309 		break;
    310 	default:
    311 		abort();
    312 	}
    313 
    314 	savelit = MANT_LITERAL & mt->fl;
    315 	mt->fl &= ~MANT_LITERAL;
    316 
    317 	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
    318 		term_fontrepl(p, font[i]);
    319 		if (savelit && NULL == nn->next)
    320 			mt->fl |= MANT_LITERAL;
    321 		print_man_node(p, mt, nn, meta);
    322 		if (nn->next)
    323 			p->flags |= TERMP_NOSPACE;
    324 	}
    325 
    326 	return(0);
    327 }
    328 
    329 static int
    330 pre_B(DECL_ARGS)
    331 {
    332 
    333 	term_fontrepl(p, TERMFONT_BOLD);
    334 	return(1);
    335 }
    336 
    337 static int
    338 pre_OP(DECL_ARGS)
    339 {
    340 
    341 	term_word(p, "[");
    342 	p->flags |= TERMP_NOSPACE;
    343 
    344 	if (NULL != (n = n->child)) {
    345 		term_fontrepl(p, TERMFONT_BOLD);
    346 		term_word(p, n->string);
    347 	}
    348 	if (NULL != n && NULL != n->next) {
    349 		term_fontrepl(p, TERMFONT_UNDER);
    350 		term_word(p, n->next->string);
    351 	}
    352 
    353 	term_fontrepl(p, TERMFONT_NONE);
    354 	p->flags |= TERMP_NOSPACE;
    355 	term_word(p, "]");
    356 	return(0);
    357 }
    358 
    359 static int
    360 pre_ft(DECL_ARGS)
    361 {
    362 	const char	*cp;
    363 
    364 	if (NULL == n->child) {
    365 		term_fontlast(p);
    366 		return(0);
    367 	}
    368 
    369 	cp = n->child->string;
    370 	switch (*cp) {
    371 	case '4':
    372 		/* FALLTHROUGH */
    373 	case '3':
    374 		/* FALLTHROUGH */
    375 	case 'B':
    376 		term_fontrepl(p, TERMFONT_BOLD);
    377 		break;
    378 	case '2':
    379 		/* FALLTHROUGH */
    380 	case 'I':
    381 		term_fontrepl(p, TERMFONT_UNDER);
    382 		break;
    383 	case 'P':
    384 		term_fontlast(p);
    385 		break;
    386 	case '1':
    387 		/* FALLTHROUGH */
    388 	case 'C':
    389 		/* FALLTHROUGH */
    390 	case 'R':
    391 		term_fontrepl(p, TERMFONT_NONE);
    392 		break;
    393 	default:
    394 		break;
    395 	}
    396 	return(0);
    397 }
    398 
    399 static int
    400 pre_in(DECL_ARGS)
    401 {
    402 	struct roffsu	 su;
    403 	const char	*cp;
    404 	size_t		 v;
    405 	int		 less;
    406 
    407 	term_newln(p);
    408 
    409 	if (NULL == n->child) {
    410 		p->offset = mt->offset;
    411 		return(0);
    412 	}
    413 
    414 	cp = n->child->string;
    415 	less = 0;
    416 
    417 	if ('-' == *cp)
    418 		less = -1;
    419 	else if ('+' == *cp)
    420 		less = 1;
    421 	else
    422 		cp--;
    423 
    424 	if ( ! a2roffsu(++cp, &su, SCALE_EN))
    425 		return(0);
    426 
    427 	v = term_hspan(p, &su);
    428 
    429 	if (less < 0)
    430 		p->offset -= p->offset > v ? v : p->offset;
    431 	else if (less > 0)
    432 		p->offset += v;
    433 	else
    434 		p->offset = v;
    435 	if (p->offset > SHRT_MAX)
    436 		p->offset = term_len(p, p->defindent);
    437 
    438 	return(0);
    439 }
    440 
    441 static int
    442 pre_sp(DECL_ARGS)
    443 {
    444 	struct roffsu	 su;
    445 	int		 i, len;
    446 
    447 	if ((NULL == n->prev && n->parent)) {
    448 		switch (n->parent->tok) {
    449 		case MAN_SH:
    450 			/* FALLTHROUGH */
    451 		case MAN_SS:
    452 			/* FALLTHROUGH */
    453 		case MAN_PP:
    454 			/* FALLTHROUGH */
    455 		case MAN_LP:
    456 			/* FALLTHROUGH */
    457 		case MAN_P:
    458 			/* FALLTHROUGH */
    459 			return(0);
    460 		default:
    461 			break;
    462 		}
    463 	}
    464 
    465 	if (n->tok == MAN_br)
    466 		len = 0;
    467 	else if (n->child == NULL)
    468 		len = 1;
    469 	else {
    470 		if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
    471 			su.scale = 1.0;
    472 		len = term_vspan(p, &su);
    473 	}
    474 
    475 	if (len == 0)
    476 		term_newln(p);
    477 	else if (len < 0)
    478 		p->skipvsp -= len;
    479 	else
    480 		for (i = 0; i < len; i++)
    481 			term_vspace(p);
    482 
    483 	return(0);
    484 }
    485 
    486 static int
    487 pre_HP(DECL_ARGS)
    488 {
    489 	struct roffsu		 su;
    490 	const struct man_node	*nn;
    491 	int			 len;
    492 
    493 	switch (n->type) {
    494 	case MAN_BLOCK:
    495 		print_bvspace(p, n, mt->pardist);
    496 		return(1);
    497 	case MAN_BODY:
    498 		break;
    499 	default:
    500 		return(0);
    501 	}
    502 
    503 	if ( ! (MANT_LITERAL & mt->fl)) {
    504 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
    505 		p->trailspace = 2;
    506 	}
    507 
    508 	/* Calculate offset. */
    509 
    510 	if ((nn = n->parent->head->child) != NULL &&
    511 	    a2roffsu(nn->string, &su, SCALE_EN)) {
    512 		len = term_hspan(p, &su);
    513 		if (len < 0 && (size_t)(-len) > mt->offset)
    514 			len = -mt->offset;
    515 		else if (len > SHRT_MAX)
    516 			len = term_len(p, p->defindent);
    517 		mt->lmargin[mt->lmargincur] = len;
    518 	} else
    519 		len = mt->lmargin[mt->lmargincur];
    520 
    521 	p->offset = mt->offset;
    522 	p->rmargin = mt->offset + len;
    523 	return(1);
    524 }
    525 
    526 static void
    527 post_HP(DECL_ARGS)
    528 {
    529 
    530 	switch (n->type) {
    531 	case MAN_BODY:
    532 		term_newln(p);
    533 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
    534 		p->trailspace = 0;
    535 		p->offset = mt->offset;
    536 		p->rmargin = p->maxrmargin;
    537 		break;
    538 	default:
    539 		break;
    540 	}
    541 }
    542 
    543 static int
    544 pre_PP(DECL_ARGS)
    545 {
    546 
    547 	switch (n->type) {
    548 	case MAN_BLOCK:
    549 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
    550 		print_bvspace(p, n, mt->pardist);
    551 		break;
    552 	default:
    553 		p->offset = mt->offset;
    554 		break;
    555 	}
    556 
    557 	return(MAN_HEAD != n->type);
    558 }
    559 
    560 static int
    561 pre_IP(DECL_ARGS)
    562 {
    563 	struct roffsu		 su;
    564 	const struct man_node	*nn;
    565 	int			 len, savelit;
    566 
    567 	switch (n->type) {
    568 	case MAN_BODY:
    569 		p->flags |= TERMP_NOSPACE;
    570 		break;
    571 	case MAN_HEAD:
    572 		p->flags |= TERMP_NOBREAK;
    573 		p->trailspace = 1;
    574 		break;
    575 	case MAN_BLOCK:
    576 		print_bvspace(p, n, mt->pardist);
    577 		/* FALLTHROUGH */
    578 	default:
    579 		return(1);
    580 	}
    581 
    582 	/* Calculate the offset from the optional second argument. */
    583 	if ((nn = n->parent->head->child) != NULL &&
    584 	    (nn = nn->next) != NULL &&
    585 	    a2roffsu(nn->string, &su, SCALE_EN)) {
    586 		len = term_hspan(p, &su);
    587 		if (len < 0 && (size_t)(-len) > mt->offset)
    588 			len = -mt->offset;
    589 		else if (len > SHRT_MAX)
    590 			len = term_len(p, p->defindent);
    591 		mt->lmargin[mt->lmargincur] = len;
    592 	} else
    593 		len = mt->lmargin[mt->lmargincur];
    594 
    595 	switch (n->type) {
    596 	case MAN_HEAD:
    597 		p->offset = mt->offset;
    598 		p->rmargin = mt->offset + len;
    599 
    600 		savelit = MANT_LITERAL & mt->fl;
    601 		mt->fl &= ~MANT_LITERAL;
    602 
    603 		if (n->child)
    604 			print_man_node(p, mt, n->child, meta);
    605 
    606 		if (savelit)
    607 			mt->fl |= MANT_LITERAL;
    608 
    609 		return(0);
    610 	case MAN_BODY:
    611 		p->offset = mt->offset + len;
    612 		p->rmargin = p->maxrmargin;
    613 		break;
    614 	default:
    615 		break;
    616 	}
    617 
    618 	return(1);
    619 }
    620 
    621 static void
    622 post_IP(DECL_ARGS)
    623 {
    624 
    625 	switch (n->type) {
    626 	case MAN_HEAD:
    627 		term_flushln(p);
    628 		p->flags &= ~TERMP_NOBREAK;
    629 		p->trailspace = 0;
    630 		p->rmargin = p->maxrmargin;
    631 		break;
    632 	case MAN_BODY:
    633 		term_newln(p);
    634 		p->offset = mt->offset;
    635 		break;
    636 	default:
    637 		break;
    638 	}
    639 }
    640 
    641 static int
    642 pre_TP(DECL_ARGS)
    643 {
    644 	struct roffsu		 su;
    645 	struct man_node		*nn;
    646 	int			 len, savelit;
    647 
    648 	switch (n->type) {
    649 	case MAN_HEAD:
    650 		p->flags |= TERMP_NOBREAK;
    651 		p->trailspace = 1;
    652 		break;
    653 	case MAN_BODY:
    654 		p->flags |= TERMP_NOSPACE;
    655 		break;
    656 	case MAN_BLOCK:
    657 		print_bvspace(p, n, mt->pardist);
    658 		/* FALLTHROUGH */
    659 	default:
    660 		return(1);
    661 	}
    662 
    663 	/* Calculate offset. */
    664 
    665 	if ((nn = n->parent->head->child) != NULL &&
    666 	    nn->string != NULL && ! (MAN_LINE & nn->flags) &&
    667 	    a2roffsu(nn->string, &su, SCALE_EN)) {
    668 		len = term_hspan(p, &su);
    669 		if (len < 0 && (size_t)(-len) > mt->offset)
    670 			len = -mt->offset;
    671 		else if (len > SHRT_MAX)
    672 			len = term_len(p, p->defindent);
    673 		mt->lmargin[mt->lmargincur] = len;
    674 	} else
    675 		len = mt->lmargin[mt->lmargincur];
    676 
    677 	switch (n->type) {
    678 	case MAN_HEAD:
    679 		p->offset = mt->offset;
    680 		p->rmargin = mt->offset + len;
    681 
    682 		savelit = MANT_LITERAL & mt->fl;
    683 		mt->fl &= ~MANT_LITERAL;
    684 
    685 		/* Don't print same-line elements. */
    686 		nn = n->child;
    687 		while (NULL != nn && 0 == (MAN_LINE & nn->flags))
    688 			nn = nn->next;
    689 
    690 		while (NULL != nn) {
    691 			print_man_node(p, mt, nn, meta);
    692 			nn = nn->next;
    693 		}
    694 
    695 		if (savelit)
    696 			mt->fl |= MANT_LITERAL;
    697 		return(0);
    698 	case MAN_BODY:
    699 		p->offset = mt->offset + len;
    700 		p->rmargin = p->maxrmargin;
    701 		p->trailspace = 0;
    702 		p->flags &= ~TERMP_NOBREAK;
    703 		break;
    704 	default:
    705 		break;
    706 	}
    707 
    708 	return(1);
    709 }
    710 
    711 static void
    712 post_TP(DECL_ARGS)
    713 {
    714 
    715 	switch (n->type) {
    716 	case MAN_HEAD:
    717 		term_flushln(p);
    718 		break;
    719 	case MAN_BODY:
    720 		term_newln(p);
    721 		p->offset = mt->offset;
    722 		break;
    723 	default:
    724 		break;
    725 	}
    726 }
    727 
    728 static int
    729 pre_SS(DECL_ARGS)
    730 {
    731 	int	 i;
    732 
    733 	switch (n->type) {
    734 	case MAN_BLOCK:
    735 		mt->fl &= ~MANT_LITERAL;
    736 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
    737 		mt->offset = term_len(p, p->defindent);
    738 
    739 		/*
    740 		 * No vertical space before the first subsection
    741 		 * and after an empty subsection.
    742 		 */
    743 
    744 		do {
    745 			n = n->prev;
    746 		} while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
    747 		if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
    748 			break;
    749 
    750 		for (i = 0; i < mt->pardist; i++)
    751 			term_vspace(p);
    752 		break;
    753 	case MAN_HEAD:
    754 		term_fontrepl(p, TERMFONT_BOLD);
    755 		p->offset = term_len(p, 3);
    756 		break;
    757 	case MAN_BODY:
    758 		p->offset = mt->offset;
    759 		break;
    760 	default:
    761 		break;
    762 	}
    763 
    764 	return(1);
    765 }
    766 
    767 static void
    768 post_SS(DECL_ARGS)
    769 {
    770 
    771 	switch (n->type) {
    772 	case MAN_HEAD:
    773 		term_newln(p);
    774 		break;
    775 	case MAN_BODY:
    776 		term_newln(p);
    777 		break;
    778 	default:
    779 		break;
    780 	}
    781 }
    782 
    783 static int
    784 pre_SH(DECL_ARGS)
    785 {
    786 	int	 i;
    787 
    788 	switch (n->type) {
    789 	case MAN_BLOCK:
    790 		mt->fl &= ~MANT_LITERAL;
    791 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
    792 		mt->offset = term_len(p, p->defindent);
    793 
    794 		/*
    795 		 * No vertical space before the first section
    796 		 * and after an empty section.
    797 		 */
    798 
    799 		do {
    800 			n = n->prev;
    801 		} while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
    802 		if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
    803 			break;
    804 
    805 		for (i = 0; i < mt->pardist; i++)
    806 			term_vspace(p);
    807 		break;
    808 	case MAN_HEAD:
    809 		term_fontrepl(p, TERMFONT_BOLD);
    810 		p->offset = 0;
    811 		break;
    812 	case MAN_BODY:
    813 		p->offset = mt->offset;
    814 		break;
    815 	default:
    816 		break;
    817 	}
    818 
    819 	return(1);
    820 }
    821 
    822 static void
    823 post_SH(DECL_ARGS)
    824 {
    825 
    826 	switch (n->type) {
    827 	case MAN_HEAD:
    828 		term_newln(p);
    829 		break;
    830 	case MAN_BODY:
    831 		term_newln(p);
    832 		break;
    833 	default:
    834 		break;
    835 	}
    836 }
    837 
    838 static int
    839 pre_RS(DECL_ARGS)
    840 {
    841 	struct roffsu	 su;
    842 
    843 	switch (n->type) {
    844 	case MAN_BLOCK:
    845 		term_newln(p);
    846 		return(1);
    847 	case MAN_HEAD:
    848 		return(0);
    849 	default:
    850 		break;
    851 	}
    852 
    853 	n = n->parent->head;
    854 	n->aux = SHRT_MAX + 1;
    855 	if (n->child != NULL && a2roffsu(n->child->string, &su, SCALE_EN))
    856 		n->aux = term_hspan(p, &su);
    857 	if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
    858 		n->aux = -mt->offset;
    859 	else if (n->aux > SHRT_MAX)
    860 		n->aux = term_len(p, p->defindent);
    861 
    862 	mt->offset += n->aux;
    863 	p->offset = mt->offset;
    864 	p->rmargin = p->maxrmargin;
    865 
    866 	if (++mt->lmarginsz < MAXMARGINS)
    867 		mt->lmargincur = mt->lmarginsz;
    868 
    869 	mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
    870 	return(1);
    871 }
    872 
    873 static void
    874 post_RS(DECL_ARGS)
    875 {
    876 
    877 	switch (n->type) {
    878 	case MAN_BLOCK:
    879 		return;
    880 	case MAN_HEAD:
    881 		return;
    882 	default:
    883 		term_newln(p);
    884 		break;
    885 	}
    886 
    887 	mt->offset -= n->parent->head->aux;
    888 	p->offset = mt->offset;
    889 
    890 	if (--mt->lmarginsz < MAXMARGINS)
    891 		mt->lmargincur = mt->lmarginsz;
    892 }
    893 
    894 static int
    895 pre_UR(DECL_ARGS)
    896 {
    897 
    898 	return (MAN_HEAD != n->type);
    899 }
    900 
    901 static void
    902 post_UR(DECL_ARGS)
    903 {
    904 
    905 	if (MAN_BLOCK != n->type)
    906 		return;
    907 
    908 	term_word(p, "<");
    909 	p->flags |= TERMP_NOSPACE;
    910 
    911 	if (NULL != n->child->child)
    912 		print_man_node(p, mt, n->child->child, meta);
    913 
    914 	p->flags |= TERMP_NOSPACE;
    915 	term_word(p, ">");
    916 }
    917 
    918 static void
    919 print_man_node(DECL_ARGS)
    920 {
    921 	size_t		 rm, rmax;
    922 	int		 c;
    923 
    924 	switch (n->type) {
    925 	case MAN_TEXT:
    926 		/*
    927 		 * If we have a blank line, output a vertical space.
    928 		 * If we have a space as the first character, break
    929 		 * before printing the line's data.
    930 		 */
    931 		if ('\0' == *n->string) {
    932 			term_vspace(p);
    933 			return;
    934 		} else if (' ' == *n->string && MAN_LINE & n->flags)
    935 			term_newln(p);
    936 
    937 		term_word(p, n->string);
    938 		goto out;
    939 
    940 	case MAN_EQN:
    941 		if ( ! (n->flags & MAN_LINE))
    942 			p->flags |= TERMP_NOSPACE;
    943 		term_eqn(p, n->eqn);
    944 		if (n->next != NULL && ! (n->next->flags & MAN_LINE))
    945 			p->flags |= TERMP_NOSPACE;
    946 		return;
    947 	case MAN_TBL:
    948 		if (p->tbl.cols == NULL)
    949 			term_vspace(p);
    950 		term_tbl(p, n->span);
    951 		return;
    952 	default:
    953 		break;
    954 	}
    955 
    956 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    957 		term_fontrepl(p, TERMFONT_NONE);
    958 
    959 	c = 1;
    960 	if (termacts[n->tok].pre)
    961 		c = (*termacts[n->tok].pre)(p, mt, n, meta);
    962 
    963 	if (c && n->child)
    964 		print_man_nodelist(p, mt, n->child, meta);
    965 
    966 	if (termacts[n->tok].post)
    967 		(*termacts[n->tok].post)(p, mt, n, meta);
    968 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    969 		term_fontrepl(p, TERMFONT_NONE);
    970 
    971 out:
    972 	/*
    973 	 * If we're in a literal context, make sure that words
    974 	 * together on the same line stay together.  This is a
    975 	 * POST-printing call, so we check the NEXT word.  Since
    976 	 * -man doesn't have nested macros, we don't need to be
    977 	 * more specific than this.
    978 	 */
    979 	if (mt->fl & MANT_LITERAL &&
    980 	    ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
    981 	    (n->next == NULL || n->next->flags & MAN_LINE)) {
    982 		rm = p->rmargin;
    983 		rmax = p->maxrmargin;
    984 		p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
    985 		p->flags |= TERMP_NOSPACE;
    986 		if (n->string != NULL && *n->string != '\0')
    987 			term_flushln(p);
    988 		else
    989 			term_newln(p);
    990 		if (rm < rmax && n->parent->tok == MAN_HP) {
    991 			p->offset = rm;
    992 			p->rmargin = rmax;
    993 		} else
    994 			p->rmargin = rm;
    995 		p->maxrmargin = rmax;
    996 	}
    997 	if (MAN_EOS & n->flags)
    998 		p->flags |= TERMP_SENTENCE;
    999 }
   1000 
   1001 
   1002 static void
   1003 print_man_nodelist(DECL_ARGS)
   1004 {
   1005 
   1006 	while (n != NULL) {
   1007 		print_man_node(p, mt, n, meta);
   1008 		n = n->next;
   1009 	}
   1010 }
   1011 
   1012 static void
   1013 print_man_foot(struct termp *p, const void *arg)
   1014 {
   1015 	const struct man_meta	*meta;
   1016 	char			*title;
   1017 	size_t			 datelen, titlen;
   1018 
   1019 	meta = (const struct man_meta *)arg;
   1020 	assert(meta->title);
   1021 	assert(meta->msec);
   1022 	assert(meta->date);
   1023 
   1024 	term_fontrepl(p, TERMFONT_NONE);
   1025 
   1026 	if (meta->hasbody)
   1027 		term_vspace(p);
   1028 
   1029 	/*
   1030 	 * Temporary, undocumented option to imitate mdoc(7) output.
   1031 	 * In the bottom right corner, use the source instead of
   1032 	 * the title.
   1033 	 */
   1034 
   1035 	if ( ! p->mdocstyle) {
   1036 		if (meta->hasbody) {
   1037 			term_vspace(p);
   1038 			term_vspace(p);
   1039 		}
   1040 		mandoc_asprintf(&title, "%s(%s)",
   1041 		    meta->title, meta->msec);
   1042 	} else if (meta->source) {
   1043 		title = mandoc_strdup(meta->source);
   1044 	} else {
   1045 		title = mandoc_strdup("");
   1046 	}
   1047 	datelen = term_strlen(p, meta->date);
   1048 
   1049 	/* Bottom left corner: manual source. */
   1050 
   1051 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
   1052 	p->trailspace = 1;
   1053 	p->offset = 0;
   1054 	p->rmargin = p->maxrmargin > datelen ?
   1055 	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
   1056 
   1057 	if (meta->source)
   1058 		term_word(p, meta->source);
   1059 	term_flushln(p);
   1060 
   1061 	/* At the bottom in the middle: manual date. */
   1062 
   1063 	p->offset = p->rmargin;
   1064 	titlen = term_strlen(p, title);
   1065 	p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
   1066 	p->flags |= TERMP_NOSPACE;
   1067 
   1068 	term_word(p, meta->date);
   1069 	term_flushln(p);
   1070 
   1071 	/* Bottom right corner: manual title and section. */
   1072 
   1073 	p->flags &= ~TERMP_NOBREAK;
   1074 	p->flags |= TERMP_NOSPACE;
   1075 	p->trailspace = 0;
   1076 	p->offset = p->rmargin;
   1077 	p->rmargin = p->maxrmargin;
   1078 
   1079 	term_word(p, title);
   1080 	term_flushln(p);
   1081 	free(title);
   1082 }
   1083 
   1084 static void
   1085 print_man_head(struct termp *p, const void *arg)
   1086 {
   1087 	const struct man_meta	*meta;
   1088 	const char		*volume;
   1089 	char			*title;
   1090 	size_t			 vollen, titlen;
   1091 
   1092 	meta = (const struct man_meta *)arg;
   1093 	assert(meta->title);
   1094 	assert(meta->msec);
   1095 
   1096 	volume = NULL == meta->vol ? "" : meta->vol;
   1097 	vollen = term_strlen(p, volume);
   1098 
   1099 	/* Top left corner: manual title and section. */
   1100 
   1101 	mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
   1102 	titlen = term_strlen(p, title);
   1103 
   1104 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
   1105 	p->trailspace = 1;
   1106 	p->offset = 0;
   1107 	p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
   1108 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
   1109 	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
   1110 
   1111 	term_word(p, title);
   1112 	term_flushln(p);
   1113 
   1114 	/* At the top in the middle: manual volume. */
   1115 
   1116 	p->flags |= TERMP_NOSPACE;
   1117 	p->offset = p->rmargin;
   1118 	p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
   1119 	    p->maxrmargin - titlen : p->maxrmargin;
   1120 
   1121 	term_word(p, volume);
   1122 	term_flushln(p);
   1123 
   1124 	/* Top right corner: title and section, again. */
   1125 
   1126 	p->flags &= ~TERMP_NOBREAK;
   1127 	p->trailspace = 0;
   1128 	if (p->rmargin + titlen <= p->maxrmargin) {
   1129 		p->flags |= TERMP_NOSPACE;
   1130 		p->offset = p->rmargin;
   1131 		p->rmargin = p->maxrmargin;
   1132 		term_word(p, title);
   1133 		term_flushln(p);
   1134 	}
   1135 
   1136 	p->flags &= ~TERMP_NOSPACE;
   1137 	p->offset = 0;
   1138 	p->rmargin = p->maxrmargin;
   1139 
   1140 	/*
   1141 	 * Groff prints three blank lines before the content.
   1142 	 * Do the same, except in the temporary, undocumented
   1143 	 * mode imitating mdoc(7) output.
   1144 	 */
   1145 
   1146 	term_vspace(p);
   1147 	if ( ! p->mdocstyle) {
   1148 		term_vspace(p);
   1149 		term_vspace(p);
   1150 	}
   1151 	free(title);
   1152 }
   1153