Home | History | Annotate | Line # | Download | only in dist
man_term.c revision 1.1.1.10
      1 /*	$Vendor-Id: man_term.c,v 1.79 2010/07/07 15:04:54 kristaps Exp $ */
      2 /*
      3  * Copyright (c) 2008, 2009 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_br(DECL_ARGS);
     96 static	int		  pre_fi(DECL_ARGS);
     97 static	int		  pre_ign(DECL_ARGS);
     98 static	int		  pre_nf(DECL_ARGS);
     99 static	int		  pre_sp(DECL_ARGS);
    100 
    101 static	void		  post_IP(DECL_ARGS);
    102 static	void		  post_HP(DECL_ARGS);
    103 static	void		  post_RS(DECL_ARGS);
    104 static	void		  post_SH(DECL_ARGS);
    105 static	void		  post_SS(DECL_ARGS);
    106 static	void		  post_TP(DECL_ARGS);
    107 
    108 static	const struct termact termacts[MAN_MAX] = {
    109 	{ pre_br, NULL, MAN_NOTEXT }, /* br */
    110 	{ NULL, NULL, 0 }, /* TH */
    111 	{ pre_SH, post_SH, 0 }, /* SH */
    112 	{ pre_SS, post_SS, 0 }, /* SS */
    113 	{ pre_TP, post_TP, 0 }, /* TP */
    114 	{ pre_PP, NULL, 0 }, /* LP */
    115 	{ pre_PP, NULL, 0 }, /* PP */
    116 	{ pre_PP, NULL, 0 }, /* P */
    117 	{ pre_IP, post_IP, 0 }, /* IP */
    118 	{ pre_HP, post_HP, 0 }, /* HP */
    119 	{ NULL, NULL, 0 }, /* SM */
    120 	{ pre_B, NULL, 0 }, /* SB */
    121 	{ pre_BI, NULL, 0 }, /* BI */
    122 	{ pre_BI, NULL, 0 }, /* IB */
    123 	{ pre_RB, NULL, 0 }, /* BR */
    124 	{ pre_RB, NULL, 0 }, /* RB */
    125 	{ NULL, NULL, 0 }, /* R */
    126 	{ pre_B, NULL, 0 }, /* B */
    127 	{ pre_I, NULL, 0 }, /* I */
    128 	{ pre_RI, NULL, 0 }, /* IR */
    129 	{ pre_RI, NULL, 0 }, /* RI */
    130 	{ NULL, NULL, MAN_NOTEXT }, /* na */
    131 	{ pre_I, NULL, 0 }, /* i */
    132 	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
    133 	{ pre_nf, NULL, 0 }, /* nf */
    134 	{ pre_fi, NULL, 0 }, /* fi */
    135 	{ NULL, NULL, 0 }, /* r */
    136 	{ NULL, NULL, 0 }, /* RE */
    137 	{ pre_RS, post_RS, 0 }, /* RS */
    138 	{ pre_ign, NULL, 0 }, /* DT */
    139 	{ pre_ign, NULL, 0 }, /* UC */
    140 	{ pre_ign, NULL, 0 }, /* PD */
    141  	{ pre_sp, NULL, MAN_NOTEXT }, /* Sp */
    142  	{ pre_nf, NULL, 0 }, /* Vb */
    143  	{ pre_fi, NULL, 0 }, /* Ve */
    144 	{ pre_ign, NULL, 0 }, /* AT */
    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_fi(DECL_ARGS)
    253 {
    254 
    255 	mt->fl &= ~MANT_LITERAL;
    256 	return(1);
    257 }
    258 
    259 
    260 /* ARGSUSED */
    261 static int
    262 pre_nf(DECL_ARGS)
    263 {
    264 
    265 	mt->fl |= MANT_LITERAL;
    266 	return(MAN_Vb != n->tok);
    267 }
    268 
    269 
    270 /* ARGSUSED */
    271 static int
    272 pre_RB(DECL_ARGS)
    273 {
    274 	const struct man_node *nn;
    275 	int		 i;
    276 
    277 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    278 		if (i % 2 && MAN_RB == n->tok)
    279 			term_fontrepl(p, TERMFONT_BOLD);
    280 		else if ( ! (i % 2) && MAN_RB != n->tok)
    281 			term_fontrepl(p, TERMFONT_BOLD);
    282 		else
    283 			term_fontrepl(p, TERMFONT_NONE);
    284 
    285 		if (i > 0)
    286 			p->flags |= TERMP_NOSPACE;
    287 
    288 		print_man_node(p, mt, nn, m);
    289 	}
    290 	return(0);
    291 }
    292 
    293 
    294 /* ARGSUSED */
    295 static int
    296 pre_RI(DECL_ARGS)
    297 {
    298 	const struct man_node *nn;
    299 	int		 i;
    300 
    301 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    302 		if (i % 2 && MAN_RI == n->tok)
    303 			term_fontrepl(p, TERMFONT_UNDER);
    304 		else if ( ! (i % 2) && MAN_RI != n->tok)
    305 			term_fontrepl(p, TERMFONT_UNDER);
    306 		else
    307 			term_fontrepl(p, TERMFONT_NONE);
    308 
    309 		if (i > 0)
    310 			p->flags |= TERMP_NOSPACE;
    311 
    312 		print_man_node(p, mt, nn, m);
    313 	}
    314 	return(0);
    315 }
    316 
    317 
    318 /* ARGSUSED */
    319 static int
    320 pre_BI(DECL_ARGS)
    321 {
    322 	const struct man_node	*nn;
    323 	int			 i;
    324 
    325 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
    326 		if (i % 2 && MAN_BI == n->tok)
    327 			term_fontrepl(p, TERMFONT_UNDER);
    328 		else if (i % 2)
    329 			term_fontrepl(p, TERMFONT_BOLD);
    330 		else if (MAN_BI == n->tok)
    331 			term_fontrepl(p, TERMFONT_BOLD);
    332 		else
    333 			term_fontrepl(p, TERMFONT_UNDER);
    334 
    335 		if (i)
    336 			p->flags |= TERMP_NOSPACE;
    337 
    338 		print_man_node(p, mt, nn, m);
    339 	}
    340 	return(0);
    341 }
    342 
    343 
    344 /* ARGSUSED */
    345 static int
    346 pre_B(DECL_ARGS)
    347 {
    348 
    349 	term_fontrepl(p, TERMFONT_BOLD);
    350 	return(1);
    351 }
    352 
    353 
    354 /* ARGSUSED */
    355 static int
    356 pre_sp(DECL_ARGS)
    357 {
    358 	size_t		 i, len;
    359 
    360 	len = n->child ?
    361 		a2height(p, n->child->string) : term_len(p, 1);
    362 
    363 	if (0 == len)
    364 		term_newln(p);
    365 	for (i = 0; i <= len; i++)
    366 		term_vspace(p);
    367 
    368 	return(0);
    369 }
    370 
    371 
    372 /* ARGSUSED */
    373 static int
    374 pre_br(DECL_ARGS)
    375 {
    376 
    377 	term_newln(p);
    378 	return(0);
    379 }
    380 
    381 
    382 /* ARGSUSED */
    383 static int
    384 pre_HP(DECL_ARGS)
    385 {
    386 	size_t			 len;
    387 	int			 ival;
    388 	const struct man_node	*nn;
    389 
    390 	switch (n->type) {
    391 	case (MAN_BLOCK):
    392 		print_bvspace(p, n);
    393 		return(1);
    394 	case (MAN_BODY):
    395 		p->flags |= TERMP_NOBREAK;
    396 		p->flags |= TERMP_TWOSPACE;
    397 		break;
    398 	default:
    399 		return(0);
    400 	}
    401 
    402 	len = mt->lmargin;
    403 	ival = -1;
    404 
    405 	/* Calculate offset. */
    406 
    407 	if (NULL != (nn = n->parent->head->child))
    408 		if ((ival = a2width(p, nn->string)) >= 0)
    409 			len = (size_t)ival;
    410 
    411 	if (0 == len)
    412 		len = term_len(p, 1);
    413 
    414 	p->offset = mt->offset;
    415 	p->rmargin = mt->offset + len;
    416 
    417 	if (ival >= 0)
    418 		mt->lmargin = (size_t)ival;
    419 
    420 	return(1);
    421 }
    422 
    423 
    424 /* ARGSUSED */
    425 static void
    426 post_HP(DECL_ARGS)
    427 {
    428 
    429 	switch (n->type) {
    430 	case (MAN_BLOCK):
    431 		term_flushln(p);
    432 		break;
    433 	case (MAN_BODY):
    434 		term_flushln(p);
    435 		p->flags &= ~TERMP_NOBREAK;
    436 		p->flags &= ~TERMP_TWOSPACE;
    437 		p->offset = mt->offset;
    438 		p->rmargin = p->maxrmargin;
    439 		break;
    440 	default:
    441 		break;
    442 	}
    443 }
    444 
    445 
    446 /* ARGSUSED */
    447 static int
    448 pre_PP(DECL_ARGS)
    449 {
    450 
    451 	switch (n->type) {
    452 	case (MAN_BLOCK):
    453 		mt->lmargin = term_len(p, INDENT);
    454 		print_bvspace(p, n);
    455 		break;
    456 	default:
    457 		p->offset = mt->offset;
    458 		break;
    459 	}
    460 
    461 	return(1);
    462 }
    463 
    464 
    465 /* ARGSUSED */
    466 static int
    467 pre_IP(DECL_ARGS)
    468 {
    469 	const struct man_node	*nn;
    470 	size_t			 len;
    471 	int			 ival;
    472 
    473 	switch (n->type) {
    474 	case (MAN_BODY):
    475 		p->flags |= TERMP_NOLPAD;
    476 		p->flags |= TERMP_NOSPACE;
    477 		break;
    478 	case (MAN_HEAD):
    479 		p->flags |= TERMP_NOBREAK;
    480 		break;
    481 	case (MAN_BLOCK):
    482 		print_bvspace(p, n);
    483 		/* FALLTHROUGH */
    484 	default:
    485 		return(1);
    486 	}
    487 
    488 	len = mt->lmargin;
    489 	ival = -1;
    490 
    491 	/* Calculate offset. */
    492 
    493 	if (NULL != (nn = n->parent->head->child))
    494 		if (NULL != (nn = nn->next)) {
    495 			for ( ; nn->next; nn = nn->next)
    496 				/* Do nothing. */ ;
    497 			if ((ival = a2width(p, nn->string)) >= 0)
    498 				len = (size_t)ival;
    499 		}
    500 
    501 	switch (n->type) {
    502 	case (MAN_HEAD):
    503 		/* Handle zero-width lengths. */
    504 		if (0 == len)
    505 			len = term_len(p, 1);
    506 
    507 		p->offset = mt->offset;
    508 		p->rmargin = mt->offset + len;
    509 		if (ival < 0)
    510 			break;
    511 
    512 		/* Set the saved left-margin. */
    513 		mt->lmargin = (size_t)ival;
    514 
    515 		/* Don't print the length value. */
    516 		for (nn = n->child; nn->next; nn = nn->next)
    517 			print_man_node(p, mt, nn, m);
    518 		return(0);
    519 	case (MAN_BODY):
    520 		p->offset = mt->offset + len;
    521 		p->rmargin = p->maxrmargin;
    522 		break;
    523 	default:
    524 		break;
    525 	}
    526 
    527 	return(1);
    528 }
    529 
    530 
    531 /* ARGSUSED */
    532 static void
    533 post_IP(DECL_ARGS)
    534 {
    535 
    536 	switch (n->type) {
    537 	case (MAN_HEAD):
    538 		term_flushln(p);
    539 		p->flags &= ~TERMP_NOBREAK;
    540 		p->rmargin = p->maxrmargin;
    541 		break;
    542 	case (MAN_BODY):
    543 		term_flushln(p);
    544 		p->flags &= ~TERMP_NOLPAD;
    545 		break;
    546 	default:
    547 		break;
    548 	}
    549 }
    550 
    551 
    552 /* ARGSUSED */
    553 static int
    554 pre_TP(DECL_ARGS)
    555 {
    556 	const struct man_node	*nn;
    557 	size_t			 len;
    558 	int			 ival;
    559 
    560 	switch (n->type) {
    561 	case (MAN_HEAD):
    562 		p->flags |= TERMP_NOBREAK;
    563 		p->flags |= TERMP_TWOSPACE;
    564 		break;
    565 	case (MAN_BODY):
    566 		p->flags |= TERMP_NOLPAD;
    567 		p->flags |= TERMP_NOSPACE;
    568 		break;
    569 	case (MAN_BLOCK):
    570 		print_bvspace(p, n);
    571 		/* FALLTHROUGH */
    572 	default:
    573 		return(1);
    574 	}
    575 
    576 	len = (size_t)mt->lmargin;
    577 	ival = -1;
    578 
    579 	/* Calculate offset. */
    580 
    581 	if (NULL != (nn = n->parent->head->child)) {
    582 		while (nn && MAN_TEXT != nn->type)
    583 			nn = nn->next;
    584 		if (nn && nn->next)
    585 			if ((ival = a2width(p, nn->string)) >= 0)
    586 				len = (size_t)ival;
    587 	}
    588 
    589 	switch (n->type) {
    590 	case (MAN_HEAD):
    591 		/* Handle zero-length properly. */
    592 		if (0 == len)
    593 			len = term_len(p, 1);
    594 
    595 		p->offset = mt->offset;
    596 		p->rmargin = mt->offset + len;
    597 
    598 		/* Don't print same-line elements. */
    599 		for (nn = n->child; nn; nn = nn->next)
    600 			if (nn->line > n->line)
    601 				print_man_node(p, mt, nn, m);
    602 
    603 		if (ival >= 0)
    604 			mt->lmargin = (size_t)ival;
    605 
    606 		return(0);
    607 	case (MAN_BODY):
    608 		p->offset = mt->offset + len;
    609 		p->rmargin = p->maxrmargin;
    610 		break;
    611 	default:
    612 		break;
    613 	}
    614 
    615 	return(1);
    616 }
    617 
    618 
    619 /* ARGSUSED */
    620 static void
    621 post_TP(DECL_ARGS)
    622 {
    623 
    624 	switch (n->type) {
    625 	case (MAN_HEAD):
    626 		term_flushln(p);
    627 		p->flags &= ~TERMP_NOBREAK;
    628 		p->flags &= ~TERMP_TWOSPACE;
    629 		p->rmargin = p->maxrmargin;
    630 		break;
    631 	case (MAN_BODY):
    632 		term_flushln(p);
    633 		p->flags &= ~TERMP_NOLPAD;
    634 		break;
    635 	default:
    636 		break;
    637 	}
    638 }
    639 
    640 
    641 /* ARGSUSED */
    642 static int
    643 pre_SS(DECL_ARGS)
    644 {
    645 
    646 	switch (n->type) {
    647 	case (MAN_BLOCK):
    648 		mt->lmargin = term_len(p, INDENT);
    649 		mt->offset = term_len(p, INDENT);
    650 		/* If following a prior empty `SS', no vspace. */
    651 		if (n->prev && MAN_SS == n->prev->tok)
    652 			if (NULL == n->prev->body->child)
    653 				break;
    654 		if (NULL == n->prev)
    655 			break;
    656 		term_vspace(p);
    657 		break;
    658 	case (MAN_HEAD):
    659 		term_fontrepl(p, TERMFONT_BOLD);
    660 		p->offset = term_len(p, HALFINDENT);
    661 		break;
    662 	case (MAN_BODY):
    663 		p->offset = mt->offset;
    664 		break;
    665 	default:
    666 		break;
    667 	}
    668 
    669 	return(1);
    670 }
    671 
    672 
    673 /* ARGSUSED */
    674 static void
    675 post_SS(DECL_ARGS)
    676 {
    677 
    678 	switch (n->type) {
    679 	case (MAN_HEAD):
    680 		term_newln(p);
    681 		break;
    682 	case (MAN_BODY):
    683 		term_newln(p);
    684 		break;
    685 	default:
    686 		break;
    687 	}
    688 }
    689 
    690 
    691 /* ARGSUSED */
    692 static int
    693 pre_SH(DECL_ARGS)
    694 {
    695 
    696 	switch (n->type) {
    697 	case (MAN_BLOCK):
    698 		mt->lmargin = term_len(p, INDENT);
    699 		mt->offset = term_len(p, INDENT);
    700 		/* If following a prior empty `SH', no vspace. */
    701 		if (n->prev && MAN_SH == n->prev->tok)
    702 			if (NULL == n->prev->body->child)
    703 				break;
    704 		/* If the first macro, no vspae. */
    705 		if (NULL == n->prev)
    706 			break;
    707 		term_vspace(p);
    708 		break;
    709 	case (MAN_HEAD):
    710 		term_fontrepl(p, TERMFONT_BOLD);
    711 		p->offset = 0;
    712 		break;
    713 	case (MAN_BODY):
    714 		p->offset = mt->offset;
    715 		break;
    716 	default:
    717 		break;
    718 	}
    719 
    720 	return(1);
    721 }
    722 
    723 
    724 /* ARGSUSED */
    725 static void
    726 post_SH(DECL_ARGS)
    727 {
    728 
    729 	switch (n->type) {
    730 	case (MAN_HEAD):
    731 		term_newln(p);
    732 		break;
    733 	case (MAN_BODY):
    734 		term_newln(p);
    735 		break;
    736 	default:
    737 		break;
    738 	}
    739 }
    740 
    741 
    742 /* ARGSUSED */
    743 static int
    744 pre_RS(DECL_ARGS)
    745 {
    746 	const struct man_node	*nn;
    747 	int			 ival;
    748 
    749 	switch (n->type) {
    750 	case (MAN_BLOCK):
    751 		term_newln(p);
    752 		return(1);
    753 	case (MAN_HEAD):
    754 		return(0);
    755 	default:
    756 		break;
    757 	}
    758 
    759 	if (NULL == (nn = n->parent->head->child)) {
    760 		mt->offset = mt->lmargin + term_len(p, INDENT);
    761 		p->offset = mt->offset;
    762 		return(1);
    763 	}
    764 
    765 	if ((ival = a2width(p, nn->string)) < 0)
    766 		return(1);
    767 
    768 	mt->offset = term_len(p, INDENT) + (size_t)ival;
    769 	p->offset = mt->offset;
    770 
    771 	return(1);
    772 }
    773 
    774 
    775 /* ARGSUSED */
    776 static void
    777 post_RS(DECL_ARGS)
    778 {
    779 
    780 	switch (n->type) {
    781 	case (MAN_BLOCK):
    782 		mt->offset = mt->lmargin = term_len(p, INDENT);
    783 		break;
    784 	case (MAN_HEAD):
    785 		break;
    786 	default:
    787 		term_newln(p);
    788 		p->offset = term_len(p, INDENT);
    789 		break;
    790 	}
    791 }
    792 
    793 
    794 static void
    795 print_man_node(DECL_ARGS)
    796 {
    797 	size_t		 rm, rmax;
    798 	int		 c;
    799 
    800 	c = 1;
    801 
    802 	switch (n->type) {
    803 	case(MAN_TEXT):
    804 		if (0 == *n->string) {
    805 			term_vspace(p);
    806 			break;
    807 		}
    808 
    809 		term_word(p, n->string);
    810 
    811 		/* FIXME: this means that macro lines are munged!  */
    812 
    813 		if (MANT_LITERAL & mt->fl) {
    814 			rm = p->rmargin;
    815 			rmax = p->maxrmargin;
    816 			p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
    817 			p->flags |= TERMP_NOSPACE;
    818 			term_flushln(p);
    819 			p->rmargin = rm;
    820 			p->maxrmargin = rmax;
    821 		}
    822 		break;
    823 	default:
    824 		if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    825 			term_fontrepl(p, TERMFONT_NONE);
    826 		if (termacts[n->tok].pre)
    827 			c = (*termacts[n->tok].pre)(p, mt, n, m);
    828 		break;
    829 	}
    830 
    831 	if (c && n->child)
    832 		print_man_nodelist(p, mt, n->child, m);
    833 
    834 	if (MAN_TEXT != n->type) {
    835 		if (termacts[n->tok].post)
    836 			(*termacts[n->tok].post)(p, mt, n, m);
    837 		if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
    838 			term_fontrepl(p, TERMFONT_NONE);
    839 	}
    840 
    841 	if (MAN_EOS & n->flags)
    842 		p->flags |= TERMP_SENTENCE;
    843 }
    844 
    845 
    846 static void
    847 print_man_nodelist(DECL_ARGS)
    848 {
    849 
    850 	print_man_node(p, mt, n, m);
    851 	if ( ! n->next)
    852 		return;
    853 	print_man_nodelist(p, mt, n->next, m);
    854 }
    855 
    856 
    857 static void
    858 print_man_foot(struct termp *p, const void *arg)
    859 {
    860 	char		buf[DATESIZ];
    861 	const struct man_meta *meta;
    862 
    863 	meta = (const struct man_meta *)arg;
    864 
    865 	term_fontrepl(p, TERMFONT_NONE);
    866 
    867 	if (meta->rawdate)
    868 		strlcpy(buf, meta->rawdate, DATESIZ);
    869 	else
    870 		time2a(meta->date, buf, DATESIZ);
    871 
    872 	term_vspace(p);
    873 	term_vspace(p);
    874 	term_vspace(p);
    875 
    876 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
    877 	p->rmargin = p->maxrmargin - term_strlen(p, buf);
    878 	p->offset = 0;
    879 
    880 	if (meta->source)
    881 		term_word(p, meta->source);
    882 	if (meta->source)
    883 		term_word(p, "");
    884 	term_flushln(p);
    885 
    886 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    887 	p->offset = p->rmargin;
    888 	p->rmargin = p->maxrmargin;
    889 	p->flags &= ~TERMP_NOBREAK;
    890 
    891 	term_word(p, buf);
    892 	term_flushln(p);
    893 }
    894 
    895 
    896 static void
    897 print_man_head(struct termp *p, const void *arg)
    898 {
    899 	char		buf[BUFSIZ], title[BUFSIZ];
    900 	size_t		buflen, titlen;
    901 	const struct man_meta *m;
    902 
    903 	m = (const struct man_meta *)arg;
    904 
    905 	/*
    906 	 * Note that old groff would spit out some spaces before the
    907 	 * header.  We discontinue this strange behaviour, but at one
    908 	 * point we did so here.
    909 	 */
    910 
    911 	p->rmargin = p->maxrmargin;
    912 
    913 	p->offset = 0;
    914 	buf[0] = title[0] = '\0';
    915 
    916 	if (m->vol)
    917 		strlcpy(buf, m->vol, BUFSIZ);
    918 	buflen = term_strlen(p, buf);
    919 
    920 	snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
    921 	titlen = term_strlen(p, title);
    922 
    923 	p->offset = 0;
    924 	p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
    925 	    (p->maxrmargin -
    926 	     term_strlen(p, buf) + term_len(p, 1)) / 2 :
    927 	    p->maxrmargin - buflen;
    928 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
    929 
    930 	term_word(p, title);
    931 	term_flushln(p);
    932 
    933 	p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    934 	p->offset = p->rmargin;
    935 	p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
    936 	    p->maxrmargin - titlen : p->maxrmargin;
    937 
    938 	term_word(p, buf);
    939 	term_flushln(p);
    940 
    941 	p->flags &= ~TERMP_NOBREAK;
    942 	if (p->rmargin + titlen <= p->maxrmargin) {
    943 		p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
    944 		p->offset = p->rmargin;
    945 		p->rmargin = p->maxrmargin;
    946 		term_word(p, title);
    947 		term_flushln(p);
    948 	}
    949 
    950 	p->rmargin = p->maxrmargin;
    951 	p->offset = 0;
    952 	p->flags &= ~TERMP_NOSPACE;
    953 
    954 	/*
    955 	 * Groff likes to have some leading spaces before content.  Well
    956 	 * that's fine by me.
    957 	 */
    958 
    959 	term_vspace(p);
    960 	term_vspace(p);
    961 	term_vspace(p);
    962 }
    963