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