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