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