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