Home | History | Annotate | Line # | Download | only in dist
man_term.c revision 1.1.1.11
      1 /*	$Vendor-Id: man_term.c,v 1.84 2010/07/23 13:22:35 kristaps Exp $ */
      2 /*
      3  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps (at) bsd.lv>
      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 #ifdef HAVE_CONFIG_H
     18 #include "config.h"
     19 #endif
     20 
     21 #include <sys/types.h>
     22 
     23 #include <assert.h>
     24 #include <ctype.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 
     29 #include "mandoc.h"
     30 #include "out.h"
     31 #include "man.h"
     32 #include "term.h"
     33 #include "chars.h"
     34 #include "main.h"
     35 
     36 #define	INDENT		  7
     37 #define	HALFINDENT	  3
     38 
     39 /* FIXME: have PD set the default vspace width. */
     40 
     41 struct	mtermp {
     42 	int		  fl;
     43 #define	MANT_LITERAL	 (1 << 0)
     44 	/*
     45 	 * Default amount to indent the left margin after leading text
     46 	 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
     47 	 * indent).  This needs to be saved because `HP' and so on, if
     48 	 * not having a specified value, must default.
     49 	 *
     50 	 * Note that this is the indentation AFTER the left offset, so
     51 	 * the total offset is usually offset + lmargin.
     52 	 */
     53 	size_t		  lmargin;
     54 	/*
     55 	 * The default offset, i.e., the amount between any text and the
     56 	 * page boundary.
     57 	 */
     58 	size_t		  offset;
     59 };
     60 
     61 #define	DECL_ARGS 	  struct termp *p, \
     62 			  struct mtermp *mt, \
     63 			  const struct man_node *n, \
     64 			  const struct man_meta *m
     65 
     66 struct	termact {
     67 	int		(*pre)(DECL_ARGS);
     68 	void		(*post)(DECL_ARGS);
     69 	int		  flags;
     70 #define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
     71 };
     72 
     73 static	int		  a2width(const struct termp *, const char *);
     74 static	size_t		  a2height(const struct termp *, const char *);
     75 
     76 static	void		  print_man_nodelist(DECL_ARGS);
     77 static	void		  print_man_node(DECL_ARGS);
     78 static	void		  print_man_head(struct termp *, const void *);
     79 static	void		  print_man_foot(struct termp *, const void *);
     80 static	void		  print_bvspace(struct termp *,
     81 				const struct man_node *);
     82 
     83 static	int		  pre_B(DECL_ARGS);
     84 static	int		  pre_BI(DECL_ARGS);
     85 static	int		  pre_HP(DECL_ARGS);
     86 static	int		  pre_I(DECL_ARGS);
     87 static	int		  pre_IP(DECL_ARGS);
     88 static	int		  pre_PP(DECL_ARGS);
     89 static	int		  pre_RB(DECL_ARGS);
     90 static	int		  pre_RI(DECL_ARGS);
     91 static	int		  pre_RS(DECL_ARGS);
     92 static	int		  pre_SH(DECL_ARGS);
     93 static	int		  pre_SS(DECL_ARGS);
     94 static	int		  pre_TP(DECL_ARGS);
     95 static	int		  pre_ign(DECL_ARGS);
     96 static	int		  pre_in(DECL_ARGS);
     97 static	int		  pre_literal(DECL_ARGS);
     98 static	int		  pre_sp(DECL_ARGS);
     99 
    100 static	void		  post_IP(DECL_ARGS);
    101 static	void		  post_HP(DECL_ARGS);
    102 static	void		  post_RS(DECL_ARGS);
    103 static	void		  post_SH(DECL_ARGS);
    104 static	void		  post_SS(DECL_ARGS);
    105 static	void		  post_TP(DECL_ARGS);
    106 
    107 static	const struct termact termacts[MAN_MAX] = {
    108 	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
    109 	{ NULL, NULL, 0 }, /* TH */
    110 	{ pre_SH, post_SH, 0 }, /* SH */
    111 	{ pre_SS, post_SS, 0 }, /* SS */
    112 	{ pre_TP, post_TP, 0 }, /* TP */
    113 	{ pre_PP, NULL, 0 }, /* LP */
    114 	{ pre_PP, NULL, 0 }, /* PP */
    115 	{ pre_PP, NULL, 0 }, /* P */
    116 	{ pre_IP, post_IP, 0 }, /* IP */
    117 	{ pre_HP, post_HP, 0 }, /* HP */
    118 	{ NULL, NULL, 0 }, /* SM */
    119 	{ pre_B, NULL, 0 }, /* SB */
    120 	{ pre_BI, NULL, 0 }, /* BI */
    121 	{ pre_BI, NULL, 0 }, /* IB */
    122 	{ pre_RB, NULL, 0 }, /* BR */
    123 	{ pre_RB, NULL, 0 }, /* RB */
    124 	{ NULL, NULL, 0 }, /* R */
    125 	{ pre_B, NULL, 0 }, /* B */
    126 	{ pre_I, NULL, 0 }, /* I */
    127 	{ pre_RI, NULL, 0 }, /* IR */
    128 	{ pre_RI, NULL, 0 }, /* RI */
    129 	{ NULL, NULL, MAN_NOTEXT }, /* na */
    130 	{ pre_I, NULL, 0 }, /* i */
    131 	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
    132 	{ pre_literal, NULL, 0 }, /* nf */
    133 	{ pre_literal, NULL, 0 }, /* fi */
    134 	{ NULL, NULL, 0 }, /* r */
    135 	{ NULL, NULL, 0 }, /* RE */
    136 	{ pre_RS, post_RS, 0 }, /* RS */
    137 	{ pre_ign, NULL, 0 }, /* DT */
    138 	{ pre_ign, NULL, 0 }, /* UC */
    139 	{ pre_ign, NULL, 0 }, /* PD */
    140  	{ pre_sp, NULL, MAN_NOTEXT }, /* Sp */
    141  	{ pre_literal, NULL, 0 }, /* Vb */
    142  	{ pre_literal, NULL, 0 }, /* Ve */
    143 	{ pre_ign, NULL, 0 }, /* AT */
    144 	{ pre_in, NULL, MAN_NOTEXT }, /* in */
    145 };
    146 
    147 
    148 
    149 void
    150 terminal_man(void *arg, const struct man *man)
    151 {
    152 	struct termp		*p;
    153 	const struct man_node	*n;
    154 	const struct man_meta	*m;
    155 	struct mtermp		 mt;
    156 
    157 	p = (struct termp *)arg;
    158 
    159 	p->overstep = 0;
    160 	p->maxrmargin = p->defrmargin;
    161 	p->tabwidth = term_len(p, 5);
    162 
    163 	if (NULL == p->symtab)
    164 		switch (p->enc) {
    165 		case (TERMENC_ASCII):
    166 			p->symtab = chars_init(CHARS_ASCII);
    167 			break;
    168 		default:
    169 			abort();
    170 			/* NOTREACHED */
    171 		}
    172 
    173 	n = man_node(man);
    174 	m = man_meta(man);
    175 
    176 	term_begin(p, print_man_head, print_man_foot, m);
    177 	p->flags |= TERMP_NOSPACE;
    178 
    179 	mt.fl = 0;
    180 	mt.lmargin = term_len(p, INDENT);
    181 	mt.offset = term_len(p, INDENT);
    182 
    183 	if (n->child)
    184 		print_man_nodelist(p, &mt, n->child, m);
    185 
    186 	term_end(p);
    187 }
    188 
    189 
    190 static size_t
    191 a2height(const struct termp *p, const char *cp)
    192 {
    193 	struct roffsu	 su;
    194 
    195 	if ( ! a2roffsu(cp, &su, SCALE_VS))
    196 		SCALE_VS_INIT(&su, term_strlen(p, cp));
    197 
    198 	return(term_vspan(p, &su));
    199 }
    200 
    201 
    202 static int
    203 a2width(const struct termp *p, const char *cp)
    204 {
    205 	struct roffsu	 su;
    206 
    207 	if ( ! a2roffsu(cp, &su, SCALE_BU))
    208 		return(-1);
    209 
    210 	return((int)term_hspan(p, &su));
    211 }
    212 
    213 
    214 static void
    215 print_bvspace(struct termp *p, const struct man_node *n)
    216 {
    217 	term_newln(p);
    218 
    219 	if (NULL == n->prev)
    220 		return;
    221 
    222 	if (MAN_SS == n->prev->tok)
    223 		return;
    224 	if (MAN_SH == n->prev->tok)
    225 		return;
    226 
    227 	term_vspace(p);
    228 }
    229 
    230 
    231 /* ARGSUSED */
    232 static int
    233 pre_ign(DECL_ARGS)
    234 {
    235 
    236 	return(0);
    237 }
    238 
    239 
    240 /* ARGSUSED */
    241 static int
    242 pre_I(DECL_ARGS)
    243 {
    244 
    245 	term_fontrepl(p, TERMFONT_UNDER);
    246 	return(1);
    247 }
    248 
    249 
    250 /* ARGSUSED */
    251 static int
    252 pre_literal(DECL_ARGS)
    253 {
    254 
    255 	term_newln(p);
    256 	switch (n->tok) {
    257 	case (MAN_Vb):
    258 		/* FALLTHROUGH */
    259 	case (MAN_nf):
    260 		mt->fl |= MANT_LITERAL;
    261 		return(MAN_Vb != n->tok);
    262 	default:
    263 		mt->fl &= ~MANT_LITERAL;
    264 		break;
    265 	}
    266 
    267 	return(1);
    268 }
    269 
    270 
    271 
    272 /* ARGSUSED */
    273 static int
    274 pre_RB(DECL_ARGS)
    275 {
    276 	const struct man_node *nn;
    277 	int		 i;
    278 
    279 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    280 		if (i % 2 && MAN_RB == n->tok)
    281 			term_fontrepl(p, TERMFONT_BOLD);
    282 		else if ( ! (i % 2) && MAN_RB != n->tok)
    283 			term_fontrepl(p, TERMFONT_BOLD);
    284 		else
    285 			term_fontrepl(p, TERMFONT_NONE);
    286 
    287 		if (i > 0)
    288 			p->flags |= TERMP_NOSPACE;
    289 
    290 		print_man_node(p, mt, nn, m);
    291 	}
    292 	return(0);
    293 }
    294 
    295 
    296 /* ARGSUSED */
    297 static int
    298 pre_RI(DECL_ARGS)
    299 {
    300 	const struct man_node *nn;
    301 	int		 i;
    302 
    303 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    304 		if (i % 2 && MAN_RI == n->tok)
    305 			term_fontrepl(p, TERMFONT_UNDER);
    306 		else if ( ! (i % 2) && MAN_RI != n->tok)
    307 			term_fontrepl(p, TERMFONT_UNDER);
    308 		else
    309 			term_fontrepl(p, TERMFONT_NONE);
    310 
    311 		if (i > 0)
    312 			p->flags |= TERMP_NOSPACE;
    313 
    314 		print_man_node(p, mt, nn, m);
    315 	}
    316 	return(0);
    317 }
    318 
    319 
    320 /* ARGSUSED */
    321 static int
    322 pre_BI(DECL_ARGS)
    323 {
    324 	const struct man_node	*nn;
    325 	int			 i;
    326 
    327 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    328 		if (i % 2 && MAN_BI == n->tok)
    329 			term_fontrepl(p, TERMFONT_UNDER);
    330 		else if (i % 2)
    331 			term_fontrepl(p, TERMFONT_BOLD);
    332 		else if (MAN_BI == n->tok)
    333 			term_fontrepl(p, TERMFONT_BOLD);
    334 		else
    335 			term_fontrepl(p, TERMFONT_UNDER);
    336 
    337 		if (i)
    338 			p->flags |= TERMP_NOSPACE;
    339 
    340 		print_man_node(p, mt, nn, m);
    341 	}
    342 	return(0);
    343 }
    344 
    345 
    346 /* ARGSUSED */
    347 static int
    348 pre_B(DECL_ARGS)
    349 {
    350 
    351 	term_fontrepl(p, TERMFONT_BOLD);
    352 	return(1);
    353 }
    354 
    355 
    356 /* ARGSUSED */
    357 static int
    358 pre_in(DECL_ARGS)
    359 {
    360 	int		 len, less;
    361 	size_t		 v;
    362 	const char	*cp;
    363 
    364 	term_newln(p);
    365 
    366 	if (NULL == n->child) {
    367 		p->offset = mt->offset;
    368 		return(0);
    369 	}
    370 
    371 	cp = n->child->string;
    372 	less = 0;
    373 
    374 	if ('-' == *cp)
    375 		less = -1;
    376 	else if ('+' == *cp)
    377 		less = 1;
    378 	else
    379 		cp--;
    380 
    381 	if ((len = a2width(p, ++cp)) < 0)
    382 		return(0);
    383 
    384 	v = (size_t)len;
    385 
    386 	if (less < 0)
    387 		p->offset -= p->offset > v ? v : p->offset;
    388 	else if (less > 0)
    389 		p->offset += v;
    390 	else
    391 		p->offset = v;
    392 
    393 	return(0);
    394 }
    395 
    396 
    397 /* ARGSUSED */
    398 static int
    399 pre_sp(DECL_ARGS)
    400 {
    401 	size_t		 i, len;
    402 
    403 	switch (n->tok) {
    404 	case (MAN_br):
    405 		len = 0;
    406 		break;
    407 	default:
    408 		len = n->child ? a2height(p, n->child->string) : 1;
    409 		break;
    410 	}
    411 
    412 	if (0 == len)
    413 		term_newln(p);
    414 	for (i = 0; i < len; i++)
    415 		term_vspace(p);
    416 
    417 	return(0);
    418 }
    419 
    420 
    421 /* ARGSUSED */
    422 static int
    423 pre_HP(DECL_ARGS)
    424 {
    425 	size_t			 len;
    426 	int			 ival;
    427 	const struct man_node	*nn;
    428 
    429 	switch (n->type) {
    430 	case (MAN_BLOCK):
    431 		print_bvspace(p, n);
    432 		return(1);
    433 	case (MAN_BODY):
    434 		p->flags |= TERMP_NOBREAK;
    435 		p->flags |= TERMP_TWOSPACE;
    436 		break;
    437 	default:
    438 		return(0);
    439 	}
    440 
    441 	len = mt->lmargin;
    442 	ival = -1;
    443 
    444 	/* Calculate offset. */
    445 
    446 	if (NULL != (nn = n->parent->head->child))
    447 		if ((ival = a2width(p, nn->string)) >= 0)
    448 			len = (size_t)ival;
    449 
    450 	if (0 == len)
    451 		len = term_len(p, 1);
    452 
    453 	p->offset = mt->offset;
    454 	p->rmargin = mt->offset + len;
    455 
    456 	if (ival >= 0)
    457 		mt->lmargin = (size_t)ival;
    458 
    459 	return(1);
    460 }
    461 
    462 
    463 /* ARGSUSED */
    464 static void
    465 post_HP(DECL_ARGS)
    466 {
    467 
    468 	switch (n->type) {
    469 	case (MAN_BLOCK):
    470 		term_flushln(p);
    471 		break;
    472 	case (MAN_BODY):
    473 		term_flushln(p);
    474 		p->flags &= ~TERMP_NOBREAK;
    475 		p->flags &= ~TERMP_TWOSPACE;
    476 		p->offset = mt->offset;
    477 		p->rmargin = p->maxrmargin;
    478 		break;
    479 	default:
    480 		break;
    481 	}
    482 }
    483 
    484 
    485 /* ARGSUSED */
    486 static int
    487 pre_PP(DECL_ARGS)
    488 {
    489 
    490 	switch (n->type) {
    491 	case (MAN_BLOCK):
    492 		mt->lmargin = term_len(p, INDENT);
    493 		print_bvspace(p, n);
    494 		break;
    495 	default:
    496 		p->offset = mt->offset;
    497 		break;
    498 	}
    499 
    500 	return(1);
    501 }
    502 
    503 
    504 /* ARGSUSED */
    505 static int
    506 pre_IP(DECL_ARGS)
    507 {
    508 	const struct man_node	*nn;
    509 	size_t			 len;
    510 	int			 ival;
    511 
    512 	switch (n->type) {
    513 	case (MAN_BODY):
    514 		p->flags |= TERMP_NOLPAD;
    515 		p->flags |= TERMP_NOSPACE;
    516 		break;
    517 	case (MAN_HEAD):
    518 		p->flags |= TERMP_NOBREAK;
    519 		break;
    520 	case (MAN_BLOCK):
    521 		print_bvspace(p, n);
    522 		/* FALLTHROUGH */
    523 	default:
    524 		return(1);
    525 	}
    526 
    527 	len = mt->lmargin;
    528 	ival = -1;
    529 
    530 	/* Calculate offset. */
    531 
    532 	if (NULL != (nn = n->parent->head->child))
    533 		if (NULL != (nn = nn->next)) {
    534 			for ( ; nn->next; nn = nn->next)
    535 				/* Do nothing. */ ;
    536 			if ((ival = a2width(p, nn->string)) >= 0)
    537 				len = (size_t)ival;
    538 		}
    539 
    540 	switch (n->type) {
    541 	case (MAN_HEAD):
    542 		/* Handle zero-width lengths. */
    543 		if (0 == len)
    544 			len = term_len(p, 1);
    545 
    546 		p->offset = mt->offset;
    547 		p->rmargin = mt->offset + len;
    548 		if (ival < 0)
    549 			break;
    550 
    551 		/* Set the saved left-margin. */
    552 		mt->lmargin = (size_t)ival;
    553 
    554 		/* Don't print the length value. */
    555 		for (nn = n->child; nn->next; nn = nn->next)
    556 			print_man_node(p, mt, nn, m);
    557 		return(0);
    558 	case (MAN_BODY):
    559 		p->offset = mt->offset + len;
    560 		p->rmargin = p->maxrmargin;
    561 		break;
    562 	default:
    563 		break;
    564 	}
    565 
    566 	return(1);
    567 }
    568 
    569 
    570 /* ARGSUSED */
    571 static void
    572 post_IP(DECL_ARGS)
    573 {
    574 
    575 	switch (n->type) {
    576 	case (MAN_HEAD):
    577 		term_flushln(p);
    578 		p->flags &= ~TERMP_NOBREAK;
    579 		p->rmargin = p->maxrmargin;
    580 		break;
    581 	case (MAN_BODY):
    582 		term_flushln(p);
    583 		p->flags &= ~TERMP_NOLPAD;
    584 		break;
    585 	default:
    586 		break;
    587 	}
    588 }
    589 
    590 
    591 /* ARGSUSED */
    592 static int
    593 pre_TP(DECL_ARGS)
    594 {
    595 	const struct man_node	*nn;
    596 	size_t			 len;
    597 	int			 ival;
    598 
    599 	switch (n->type) {
    600 	case (MAN_HEAD):
    601 		p->flags |= TERMP_NOBREAK;
    602 		p->flags |= TERMP_TWOSPACE;
    603 		break;
    604 	case (MAN_BODY):
    605 		p->flags |= TERMP_NOLPAD;
    606 		p->flags |= TERMP_NOSPACE;
    607 		break;
    608 	case (MAN_BLOCK):
    609 		print_bvspace(p, n);
    610 		/* FALLTHROUGH */
    611 	default:
    612 		return(1);
    613 	}
    614 
    615 	len = (size_t)mt->lmargin;
    616 	ival = -1;
    617 
    618 	/* Calculate offset. */
    619 
    620 	if (NULL != (nn = n->parent->head->child)) {
    621 		while (nn && MAN_TEXT != nn->type)
    622 			nn = nn->next;
    623 		if (nn && nn->next)
    624 			if ((ival = a2width(p, nn->string)) >= 0)
    625 				len = (size_t)ival;
    626 	}
    627 
    628 	switch (n->type) {
    629 	case (MAN_HEAD):
    630 		/* Handle zero-length properly. */
    631 		if (0 == len)
    632 			len = term_len(p, 1);
    633 
    634 		p->offset = mt->offset;
    635 		p->rmargin = mt->offset + len;
    636 
    637 		/* Don't print same-line elements. */
    638 		for (nn = n->child; nn; nn = nn->next)
    639 			if (nn->line > n->line)
    640 				print_man_node(p, mt, nn, m);
    641 
    642 		if (ival >= 0)
    643 			mt->lmargin = (size_t)ival;
    644 
    645 		return(0);
    646 	case (MAN_BODY):
    647 		p->offset = mt->offset + len;
    648 		p->rmargin = p->maxrmargin;
    649 		break;
    650 	default:
    651 		break;
    652 	}
    653 
    654 	return(1);
    655 }
    656 
    657 
    658 /* ARGSUSED */
    659 static void
    660 post_TP(DECL_ARGS)
    661 {
    662 
    663 	switch (n->type) {
    664 	case (MAN_HEAD):
    665 		term_flushln(p);
    666 		p->flags &= ~TERMP_NOBREAK;
    667 		p->flags &= ~TERMP_TWOSPACE;
    668 		p->rmargin = p->maxrmargin;
    669 		break;
    670 	case (MAN_BODY):
    671 		term_flushln(p);
    672 		p->flags &= ~TERMP_NOLPAD;
    673 		break;
    674 	default:
    675 		break;
    676 	}
    677 }
    678 
    679 
    680 /* ARGSUSED */
    681 static int
    682 pre_SS(DECL_ARGS)
    683 {
    684 
    685 	switch (n->type) {
    686 	case (MAN_BLOCK):
    687 		mt->lmargin = term_len(p, INDENT);
    688 		mt->offset = term_len(p, INDENT);
    689 		/* If following a prior empty `SS', no vspace. */
    690 		if (n->prev && MAN_SS == n->prev->tok)
    691 			if (NULL == n->prev->body->child)
    692 				break;
    693 		if (NULL == n->prev)
    694 			break;
    695 		term_vspace(p);
    696 		break;
    697 	case (MAN_HEAD):
    698 		term_fontrepl(p, TERMFONT_BOLD);
    699 		p->offset = term_len(p, HALFINDENT);
    700 		break;
    701 	case (MAN_BODY):
    702 		p->offset = mt->offset;
    703 		break;
    704 	default:
    705 		break;
    706 	}
    707 
    708 	return(1);
    709 }
    710 
    711 
    712 /* ARGSUSED */
    713 static void
    714 post_SS(DECL_ARGS)
    715 {
    716 
    717 	switch (n->type) {
    718 	case (MAN_HEAD):
    719 		term_newln(p);
    720 		break;
    721 	case (MAN_BODY):
    722 		term_newln(p);
    723 		break;
    724 	default:
    725 		break;
    726 	}
    727 }
    728 
    729 
    730 /* ARGSUSED */
    731 static int
    732 pre_SH(DECL_ARGS)
    733 {
    734 
    735 	switch (n->type) {
    736 	case (MAN_BLOCK):
    737 		mt->lmargin = term_len(p, INDENT);
    738 		mt->offset = term_len(p, INDENT);
    739 		/* If following a prior empty `SH', no vspace. */
    740 		if (n->prev && MAN_SH == n->prev->tok)
    741 			if (NULL == n->prev->body->child)
    742 				break;
    743 		/* If the first macro, no vspae. */
    744 		if (NULL == n->prev)
    745 			break;
    746 		term_vspace(p);
    747 		break;
    748 	case (MAN_HEAD):
    749 		term_fontrepl(p, TERMFONT_BOLD);
    750 		p->offset = 0;
    751 		break;
    752 	case (MAN_BODY):
    753 		p->offset = mt->offset;
    754 		break;
    755 	default:
    756 		break;
    757 	}
    758 
    759 	return(1);
    760 }
    761 
    762 
    763 /* ARGSUSED */
    764 static void
    765 post_SH(DECL_ARGS)
    766 {
    767 
    768 	switch (n->type) {
    769 	case (MAN_HEAD):
    770 		term_newln(p);
    771 		break;
    772 	case (MAN_BODY):
    773 		term_newln(p);
    774 		break;
    775 	default:
    776 		break;
    777 	}
    778 }
    779 
    780 
    781 /* ARGSUSED */
    782 static int
    783 pre_RS(DECL_ARGS)
    784 {
    785 	const struct man_node	*nn;
    786 	int			 ival;
    787 
    788 	switch (n->type) {
    789 	case (MAN_BLOCK):
    790 		term_newln(p);
    791 		return(1);
    792 	case (MAN_HEAD):
    793 		return(0);
    794 	default:
    795 		break;
    796 	}
    797 
    798 	if (NULL == (nn = n->parent->head->child)) {
    799 		mt->offset = mt->lmargin + term_len(p, INDENT);
    800 		p->offset = mt->offset;
    801 		return(1);
    802 	}
    803 
    804 	if ((ival = a2width(p, nn->string)) < 0)
    805 		return(1);
    806 
    807 	mt->offset = term_len(p, INDENT) + (size_t)ival;
    808 	p->offset = mt->offset;
    809 
    810 	return(1);
    811 }
    812 
    813 
    814 /* ARGSUSED */
    815 static void
    816 post_RS(DECL_ARGS)
    817 {
    818 
    819 	switch (n->type) {
    820 	case (MAN_BLOCK):
    821 		mt->offset = mt->lmargin = term_len(p, INDENT);
    822 		break;
    823 	case (MAN_HEAD):
    824 		break;
    825 	default:
    826 		term_newln(p);
    827 		p->offset = term_len(p, INDENT);
    828 		break;
    829 	}
    830 }
    831 
    832 
    833 static void
    834 print_man_node(DECL_ARGS)
    835 {
    836 	size_t		 rm, rmax;
    837 	int		 c;
    838 
    839 	c = 1;
    840 
    841 	switch (n->type) {
    842 	case(MAN_TEXT):
    843 		if (0 == *n->string) {
    844 			term_vspace(p);
    845 			break;
    846 		}
    847 
    848 		term_word(p, n->string);
    849 
    850 		/* FIXME: this means that macro lines are munged!  */
    851 
    852 		if (MANT_LITERAL & mt->fl) {
    853 			rm = p->rmargin;
    854 			rmax = p->maxrmargin;
    855 			p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
    856 			p->flags |= TERMP_NOSPACE;
    857 			term_flushln(p);
    858 			p->rmargin = rm;
    859 			p->maxrmargin = rmax;
    860 		}
    861 		break;
    862 	default:
    863 		if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    864 			term_fontrepl(p, TERMFONT_NONE);
    865 		if (termacts[n->tok].pre)
    866 			c = (*termacts[n->tok].pre)(p, mt, n, m);
    867 		break;
    868 	}
    869 
    870 	if (c && n->child)
    871 		print_man_nodelist(p, mt, n->child, m);
    872 
    873 	if (MAN_TEXT != n->type) {
    874 		if (termacts[n->tok].post)
    875 			(*termacts[n->tok].post)(p, mt, n, m);
    876 		if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    877 			term_fontrepl(p, TERMFONT_NONE);
    878 	}
    879 
    880 	if (MAN_EOS & n->flags)
    881 		p->flags |= TERMP_SENTENCE;
    882 }
    883 
    884 
    885 static void
    886 print_man_nodelist(DECL_ARGS)
    887 {
    888 
    889 	print_man_node(p, mt, n, m);
    890 	if ( ! n->next)
    891 		return;
    892 	print_man_nodelist(p, mt, n->next, m);
    893 }
    894 
    895 
    896 static void
    897 print_man_foot(struct termp *p, const void *arg)
    898 {
    899 	char		buf[DATESIZ];
    900 	const struct man_meta *meta;
    901 
    902 	meta = (const struct man_meta *)arg;
    903 
    904 	term_fontrepl(p, TERMFONT_NONE);
    905 
    906 	if (meta->rawdate)
    907 		strlcpy(buf, meta->rawdate, DATESIZ);
    908 	else
    909 		time2a(meta->date, buf, DATESIZ);
    910 
    911 	term_vspace(p);
    912 	term_vspace(p);
    913 	term_vspace(p);
    914 
    915 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
    916 	p->rmargin = p->maxrmargin - term_strlen(p, buf);
    917 	p->offset = 0;
    918 
    919 	if (meta->source)
    920 		term_word(p, meta->source);
    921 	if (meta->source)
    922 		term_word(p, "");
    923 	term_flushln(p);
    924 
    925 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    926 	p->offset = p->rmargin;
    927 	p->rmargin = p->maxrmargin;
    928 	p->flags &= ~TERMP_NOBREAK;
    929 
    930 	term_word(p, buf);
    931 	term_flushln(p);
    932 }
    933 
    934 
    935 static void
    936 print_man_head(struct termp *p, const void *arg)
    937 {
    938 	char		buf[BUFSIZ], title[BUFSIZ];
    939 	size_t		buflen, titlen;
    940 	const struct man_meta *m;
    941 
    942 	m = (const struct man_meta *)arg;
    943 
    944 	/*
    945 	 * Note that old groff would spit out some spaces before the
    946 	 * header.  We discontinue this strange behaviour, but at one
    947 	 * point we did so here.
    948 	 */
    949 
    950 	p->rmargin = p->maxrmargin;
    951 
    952 	p->offset = 0;
    953 	buf[0] = title[0] = '\0';
    954 
    955 	if (m->vol)
    956 		strlcpy(buf, m->vol, BUFSIZ);
    957 	buflen = term_strlen(p, buf);
    958 
    959 	snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
    960 	titlen = term_strlen(p, title);
    961 
    962 	p->offset = 0;
    963 	p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
    964 	    (p->maxrmargin -
    965 	     term_strlen(p, buf) + term_len(p, 1)) / 2 :
    966 	    p->maxrmargin - buflen;
    967 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
    968 
    969 	term_word(p, title);
    970 	term_flushln(p);
    971 
    972 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    973 	p->offset = p->rmargin;
    974 	p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
    975 	    p->maxrmargin - titlen : p->maxrmargin;
    976 
    977 	term_word(p, buf);
    978 	term_flushln(p);
    979 
    980 	p->flags &= ~TERMP_NOBREAK;
    981 	if (p->rmargin + titlen <= p->maxrmargin) {
    982 		p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    983 		p->offset = p->rmargin;
    984 		p->rmargin = p->maxrmargin;
    985 		term_word(p, title);
    986 		term_flushln(p);
    987 	}
    988 
    989 	p->rmargin = p->maxrmargin;
    990 	p->offset = 0;
    991 	p->flags &= ~TERMP_NOSPACE;
    992 
    993 	/*
    994 	 * Groff likes to have some leading spaces before content.  Well
    995 	 * that's fine by me.
    996 	 */
    997 
    998 	term_vspace(p);
    999 	term_vspace(p);
   1000 	term_vspace(p);
   1001 }
   1002