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