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