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