xref: /dragonfly/contrib/mdocml/man_term.c (revision 99db7d0e)
1*99db7d0eSSascha Wildner /* $Id: man_term.c,v 1.236 2021/06/28 19:50:15 schwarze Exp $ */
280387638SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org>
4f88b6c16SFranco Fichtner  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
580387638SSascha Wildner  *
680387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
780387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
880387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
980387638SSascha Wildner  *
1054ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1180387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1380387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1480387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1580387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1680387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*99db7d0eSSascha Wildner  *
18*99db7d0eSSascha Wildner  * Plain text formatter for man(7), used by mandoc(1)
19*99db7d0eSSascha Wildner  * for ASCII, UTF-8, PostScript, and PDF output.
2080387638SSascha Wildner  */
2180387638SSascha Wildner #include "config.h"
2280387638SSascha Wildner 
2380387638SSascha Wildner #include <sys/types.h>
2480387638SSascha Wildner 
2580387638SSascha Wildner #include <assert.h>
2680387638SSascha Wildner #include <ctype.h>
2754ba9607SSascha Wildner #include <limits.h>
2880387638SSascha Wildner #include <stdio.h>
2980387638SSascha Wildner #include <stdlib.h>
3080387638SSascha Wildner #include <string.h>
3180387638SSascha Wildner 
32070c62a6SFranco Fichtner #include "mandoc_aux.h"
33*99db7d0eSSascha Wildner #include "mandoc.h"
3454ba9607SSascha Wildner #include "roff.h"
3580387638SSascha Wildner #include "man.h"
3654ba9607SSascha Wildner #include "out.h"
3780387638SSascha Wildner #include "term.h"
38*99db7d0eSSascha Wildner #include "term_tag.h"
3980387638SSascha Wildner #include "main.h"
4080387638SSascha Wildner 
4136342e81SSascha Wildner #define	MAXMARGINS	  64 /* maximum number of indented scopes */
4280387638SSascha Wildner 
4380387638SSascha Wildner struct	mtermp {
4454ba9607SSascha Wildner 	int		  lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
4536342e81SSascha Wildner 	int		  lmargincur; /* index of current margin */
4636342e81SSascha Wildner 	int		  lmarginsz; /* actual number of nested margins */
4736342e81SSascha Wildner 	size_t		  offset; /* default offset to visible page */
48f88b6c16SFranco Fichtner 	int		  pardist; /* vert. space before par., unit: [v] */
4980387638SSascha Wildner };
5080387638SSascha Wildner 
5180387638SSascha Wildner #define	DECL_ARGS	  struct termp *p, \
5280387638SSascha Wildner 			  struct mtermp *mt, \
5354ba9607SSascha Wildner 			  struct roff_node *n, \
5454ba9607SSascha Wildner 			  const struct roff_meta *meta
5580387638SSascha Wildner 
5654ba9607SSascha Wildner struct	man_term_act {
5780387638SSascha Wildner 	int		(*pre)(DECL_ARGS);
5880387638SSascha Wildner 	void		(*post)(DECL_ARGS);
5980387638SSascha Wildner 	int		  flags;
6080387638SSascha Wildner #define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
6180387638SSascha Wildner };
6280387638SSascha Wildner 
6380387638SSascha Wildner static	void		  print_man_nodelist(DECL_ARGS);
6480387638SSascha Wildner static	void		  print_man_node(DECL_ARGS);
6554ba9607SSascha Wildner static	void		  print_man_head(struct termp *,
6654ba9607SSascha Wildner 				const struct roff_meta *);
6754ba9607SSascha Wildner static	void		  print_man_foot(struct termp *,
6854ba9607SSascha Wildner 				const struct roff_meta *);
6980387638SSascha Wildner static	void		  print_bvspace(struct termp *,
70*99db7d0eSSascha Wildner 				struct roff_node *, int);
7180387638SSascha Wildner 
7280387638SSascha Wildner static	int		  pre_B(DECL_ARGS);
7354ba9607SSascha Wildner static	int		  pre_DT(DECL_ARGS);
7480387638SSascha Wildner static	int		  pre_HP(DECL_ARGS);
7580387638SSascha Wildner static	int		  pre_I(DECL_ARGS);
7680387638SSascha Wildner static	int		  pre_IP(DECL_ARGS);
7736342e81SSascha Wildner static	int		  pre_OP(DECL_ARGS);
78f88b6c16SFranco Fichtner static	int		  pre_PD(DECL_ARGS);
7980387638SSascha Wildner static	int		  pre_PP(DECL_ARGS);
8080387638SSascha Wildner static	int		  pre_RS(DECL_ARGS);
8180387638SSascha Wildner static	int		  pre_SH(DECL_ARGS);
8280387638SSascha Wildner static	int		  pre_SS(DECL_ARGS);
8354ba9607SSascha Wildner static	int		  pre_SY(DECL_ARGS);
8480387638SSascha Wildner static	int		  pre_TP(DECL_ARGS);
857888c61dSFranco Fichtner static	int		  pre_UR(DECL_ARGS);
8654ba9607SSascha Wildner static	int		  pre_abort(DECL_ARGS);
8736342e81SSascha Wildner static	int		  pre_alternate(DECL_ARGS);
8880387638SSascha Wildner static	int		  pre_ign(DECL_ARGS);
8980387638SSascha Wildner static	int		  pre_in(DECL_ARGS);
9080387638SSascha Wildner static	int		  pre_literal(DECL_ARGS);
9180387638SSascha Wildner 
9280387638SSascha Wildner static	void		  post_IP(DECL_ARGS);
9380387638SSascha Wildner static	void		  post_HP(DECL_ARGS);
9480387638SSascha Wildner static	void		  post_RS(DECL_ARGS);
9580387638SSascha Wildner static	void		  post_SH(DECL_ARGS);
9654ba9607SSascha Wildner static	void		  post_SY(DECL_ARGS);
9780387638SSascha Wildner static	void		  post_TP(DECL_ARGS);
987888c61dSFranco Fichtner static	void		  post_UR(DECL_ARGS);
9980387638SSascha Wildner 
10054ba9607SSascha Wildner static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
10180387638SSascha Wildner 	{ NULL, NULL, 0 }, /* TH */
10280387638SSascha Wildner 	{ pre_SH, post_SH, 0 }, /* SH */
10354ba9607SSascha Wildner 	{ pre_SS, post_SH, 0 }, /* SS */
10480387638SSascha Wildner 	{ pre_TP, post_TP, 0 }, /* TP */
10554ba9607SSascha Wildner 	{ pre_TP, post_TP, 0 }, /* TQ */
10654ba9607SSascha Wildner 	{ pre_abort, NULL, 0 }, /* LP */
10780387638SSascha Wildner 	{ pre_PP, NULL, 0 }, /* PP */
10854ba9607SSascha Wildner 	{ pre_abort, NULL, 0 }, /* P */
10980387638SSascha Wildner 	{ pre_IP, post_IP, 0 }, /* IP */
11080387638SSascha Wildner 	{ pre_HP, post_HP, 0 }, /* HP */
11180387638SSascha Wildner 	{ NULL, NULL, 0 }, /* SM */
11280387638SSascha Wildner 	{ pre_B, NULL, 0 }, /* SB */
11380387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* BI */
11480387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* IB */
11580387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* BR */
11680387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* RB */
11780387638SSascha Wildner 	{ NULL, NULL, 0 }, /* R */
11880387638SSascha Wildner 	{ pre_B, NULL, 0 }, /* B */
11980387638SSascha Wildner 	{ pre_I, NULL, 0 }, /* I */
12080387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* IR */
12180387638SSascha Wildner 	{ pre_alternate, NULL, 0 }, /* RI */
12280387638SSascha Wildner 	{ NULL, NULL, 0 }, /* RE */
12380387638SSascha Wildner 	{ pre_RS, post_RS, 0 }, /* RS */
12454ba9607SSascha Wildner 	{ pre_DT, NULL, 0 }, /* DT */
12554ba9607SSascha Wildner 	{ pre_ign, NULL, MAN_NOTEXT }, /* UC */
126f88b6c16SFranco Fichtner 	{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
12780387638SSascha Wildner 	{ pre_ign, NULL, 0 }, /* AT */
12880387638SSascha Wildner 	{ pre_in, NULL, MAN_NOTEXT }, /* in */
12954ba9607SSascha Wildner 	{ pre_SY, post_SY, 0 }, /* SY */
13054ba9607SSascha Wildner 	{ NULL, NULL, 0 }, /* YS */
13136342e81SSascha Wildner 	{ pre_OP, NULL, 0 }, /* OP */
132f88b6c16SFranco Fichtner 	{ pre_literal, NULL, 0 }, /* EX */
133f88b6c16SFranco Fichtner 	{ pre_literal, NULL, 0 }, /* EE */
1347888c61dSFranco Fichtner 	{ pre_UR, post_UR, 0 }, /* UR */
1357888c61dSFranco Fichtner 	{ NULL, NULL, 0 }, /* UE */
13654ba9607SSascha Wildner 	{ pre_UR, post_UR, 0 }, /* MT */
13754ba9607SSascha Wildner 	{ NULL, NULL, 0 }, /* ME */
13880387638SSascha Wildner };
13954ba9607SSascha Wildner static const struct man_term_act *man_term_act(enum roff_tok);
14080387638SSascha Wildner 
14180387638SSascha Wildner 
14254ba9607SSascha Wildner static const struct man_term_act *
man_term_act(enum roff_tok tok)14354ba9607SSascha Wildner man_term_act(enum roff_tok tok)
14454ba9607SSascha Wildner {
14554ba9607SSascha Wildner 	assert(tok >= MAN_TH && tok <= MAN_MAX);
14654ba9607SSascha Wildner 	return man_term_acts + (tok - MAN_TH);
14754ba9607SSascha Wildner }
14854ba9607SSascha Wildner 
14980387638SSascha Wildner void
terminal_man(void * arg,const struct roff_meta * man)15054ba9607SSascha Wildner terminal_man(void *arg, const struct roff_meta *man)
15180387638SSascha Wildner {
15280387638SSascha Wildner 	struct mtermp		 mt;
15354ba9607SSascha Wildner 	struct termp		*p;
154*99db7d0eSSascha Wildner 	struct roff_node	*n, *nc, *nn;
15554ba9607SSascha Wildner 	size_t			 save_defindent;
15680387638SSascha Wildner 
15780387638SSascha Wildner 	p = (struct termp *)arg;
15854ba9607SSascha Wildner 	save_defindent = p->defindent;
15954ba9607SSascha Wildner 	if (p->synopsisonly == 0 && p->defindent == 0)
16036342e81SSascha Wildner 		p->defindent = 7;
16154ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
16254ba9607SSascha Wildner 	term_tab_set(p, NULL);
16354ba9607SSascha Wildner 	term_tab_set(p, "T");
16454ba9607SSascha Wildner 	term_tab_set(p, ".5i");
16536342e81SSascha Wildner 
16654ba9607SSascha Wildner 	memset(&mt, 0, sizeof(mt));
16736342e81SSascha Wildner 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
16836342e81SSascha Wildner 	mt.offset = term_len(p, p->defindent);
169f88b6c16SFranco Fichtner 	mt.pardist = 1;
17080387638SSascha Wildner 
17154ba9607SSascha Wildner 	n = man->first->child;
17254ba9607SSascha Wildner 	if (p->synopsisonly) {
173*99db7d0eSSascha Wildner 		for (nn = NULL; n != NULL; n = n->next) {
174*99db7d0eSSascha Wildner 			if (n->tok != MAN_SH)
175*99db7d0eSSascha Wildner 				continue;
176*99db7d0eSSascha Wildner 			nc = n->child->child;
177*99db7d0eSSascha Wildner 			if (nc->type != ROFFT_TEXT)
178*99db7d0eSSascha Wildner 				continue;
179*99db7d0eSSascha Wildner 			if (strcmp(nc->string, "SYNOPSIS") == 0)
18054ba9607SSascha Wildner 				break;
181*99db7d0eSSascha Wildner 			if (nn == NULL && strcmp(nc->string, "NAME") == 0)
182*99db7d0eSSascha Wildner 				nn = n;
18354ba9607SSascha Wildner 		}
184*99db7d0eSSascha Wildner 		if (n == NULL)
185*99db7d0eSSascha Wildner 			n = nn;
186*99db7d0eSSascha Wildner 		p->flags |= TERMP_NOSPACE;
187*99db7d0eSSascha Wildner 		if (n != NULL && (n = n->child->next->child) != NULL)
188*99db7d0eSSascha Wildner 			print_man_nodelist(p, &mt, n, man);
189*99db7d0eSSascha Wildner 		term_newln(p);
19054ba9607SSascha Wildner 	} else {
19154ba9607SSascha Wildner 		term_begin(p, print_man_head, print_man_foot, man);
19254ba9607SSascha Wildner 		p->flags |= TERMP_NOSPACE;
19354ba9607SSascha Wildner 		if (n != NULL)
19454ba9607SSascha Wildner 			print_man_nodelist(p, &mt, n, man);
19580387638SSascha Wildner 		term_end(p);
19680387638SSascha Wildner 	}
19754ba9607SSascha Wildner 	p->defindent = save_defindent;
19880387638SSascha Wildner }
19980387638SSascha Wildner 
20036342e81SSascha Wildner /*
20136342e81SSascha Wildner  * Printing leading vertical space before a block.
20236342e81SSascha Wildner  * This is used for the paragraph macros.
20336342e81SSascha Wildner  * The rules are pretty simple, since there's very little nesting going
20436342e81SSascha Wildner  * on here.  Basically, if we're the first within another block (SS/SH),
20536342e81SSascha Wildner  * then don't emit vertical space.  If we are (RS), then do.  If not the
20636342e81SSascha Wildner  * first, print it.
20736342e81SSascha Wildner  */
20880387638SSascha Wildner static void
print_bvspace(struct termp * p,struct roff_node * n,int pardist)209*99db7d0eSSascha Wildner print_bvspace(struct termp *p, struct roff_node *n, int pardist)
21080387638SSascha Wildner {
211*99db7d0eSSascha Wildner 	struct roff_node	*nch;
212f88b6c16SFranco Fichtner 	int			 i;
21336342e81SSascha Wildner 
21480387638SSascha Wildner 	term_newln(p);
21580387638SSascha Wildner 
216*99db7d0eSSascha Wildner 	if (n->body != NULL &&
217*99db7d0eSSascha Wildner 	    (nch = roff_node_child(n->body)) != NULL &&
218*99db7d0eSSascha Wildner 	    nch->type == ROFFT_TBL)
21960e1e752SSascha Wildner 		return;
22060e1e752SSascha Wildner 
221*99db7d0eSSascha Wildner 	if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL)
22280387638SSascha Wildner 		return;
22380387638SSascha Wildner 
224f88b6c16SFranco Fichtner 	for (i = 0; i < pardist; i++)
22580387638SSascha Wildner 		term_vspace(p);
22680387638SSascha Wildner }
22780387638SSascha Wildner 
228070c62a6SFranco Fichtner 
22980387638SSascha Wildner static int
pre_abort(DECL_ARGS)23054ba9607SSascha Wildner pre_abort(DECL_ARGS)
23180387638SSascha Wildner {
23254ba9607SSascha Wildner 	abort();
23380387638SSascha Wildner }
23480387638SSascha Wildner 
235070c62a6SFranco Fichtner static int
pre_ign(DECL_ARGS)23654ba9607SSascha Wildner pre_ign(DECL_ARGS)
237070c62a6SFranco Fichtner {
23854ba9607SSascha Wildner 	return 0;
239070c62a6SFranco Fichtner }
240070c62a6SFranco Fichtner 
24180387638SSascha Wildner static int
pre_I(DECL_ARGS)24280387638SSascha Wildner pre_I(DECL_ARGS)
24380387638SSascha Wildner {
24480387638SSascha Wildner 	term_fontrepl(p, TERMFONT_UNDER);
24554ba9607SSascha Wildner 	return 1;
24680387638SSascha Wildner }
24780387638SSascha Wildner 
24880387638SSascha Wildner static int
pre_literal(DECL_ARGS)24980387638SSascha Wildner pre_literal(DECL_ARGS)
25080387638SSascha Wildner {
25180387638SSascha Wildner 	term_newln(p);
25280387638SSascha Wildner 
25336342e81SSascha Wildner 	/*
25436342e81SSascha Wildner 	 * Unlike .IP and .TP, .HP does not have a HEAD.
25536342e81SSascha Wildner 	 * So in case a second call to term_flushln() is needed,
25636342e81SSascha Wildner 	 * indentation has to be set up explicitly.
25736342e81SSascha Wildner 	 */
25854ba9607SSascha Wildner 	if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
25954ba9607SSascha Wildner 		p->tcol->offset = p->tcol->rmargin;
26054ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
2617888c61dSFranco Fichtner 		p->trailspace = 0;
262070c62a6SFranco Fichtner 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
26336342e81SSascha Wildner 		p->flags |= TERMP_NOSPACE;
26436342e81SSascha Wildner 	}
26554ba9607SSascha Wildner 	return 0;
26680387638SSascha Wildner }
26780387638SSascha Wildner 
26880387638SSascha Wildner static int
pre_PD(DECL_ARGS)269f88b6c16SFranco Fichtner pre_PD(DECL_ARGS)
270f88b6c16SFranco Fichtner {
27154ba9607SSascha Wildner 	struct roffsu	 su;
272f88b6c16SFranco Fichtner 
273f88b6c16SFranco Fichtner 	n = n->child;
27454ba9607SSascha Wildner 	if (n == NULL) {
275f88b6c16SFranco Fichtner 		mt->pardist = 1;
27654ba9607SSascha Wildner 		return 0;
277f88b6c16SFranco Fichtner 	}
27854ba9607SSascha Wildner 	assert(n->type == ROFFT_TEXT);
27954ba9607SSascha Wildner 	if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
28054ba9607SSascha Wildner 		mt->pardist = term_vspan(p, &su);
28154ba9607SSascha Wildner 	return 0;
282f88b6c16SFranco Fichtner }
283f88b6c16SFranco Fichtner 
284f88b6c16SFranco Fichtner static int
pre_alternate(DECL_ARGS)28580387638SSascha Wildner pre_alternate(DECL_ARGS)
28680387638SSascha Wildner {
28780387638SSascha Wildner 	enum termfont		 font[2];
28854ba9607SSascha Wildner 	struct roff_node	*nn;
28954ba9607SSascha Wildner 	int			 i;
29080387638SSascha Wildner 
29180387638SSascha Wildner 	switch (n->tok) {
292070c62a6SFranco Fichtner 	case MAN_RB:
29380387638SSascha Wildner 		font[0] = TERMFONT_NONE;
29480387638SSascha Wildner 		font[1] = TERMFONT_BOLD;
29580387638SSascha Wildner 		break;
296070c62a6SFranco Fichtner 	case MAN_RI:
29780387638SSascha Wildner 		font[0] = TERMFONT_NONE;
29880387638SSascha Wildner 		font[1] = TERMFONT_UNDER;
29980387638SSascha Wildner 		break;
300070c62a6SFranco Fichtner 	case MAN_BR:
30180387638SSascha Wildner 		font[0] = TERMFONT_BOLD;
30280387638SSascha Wildner 		font[1] = TERMFONT_NONE;
30380387638SSascha Wildner 		break;
304070c62a6SFranco Fichtner 	case MAN_BI:
30580387638SSascha Wildner 		font[0] = TERMFONT_BOLD;
30680387638SSascha Wildner 		font[1] = TERMFONT_UNDER;
30780387638SSascha Wildner 		break;
308070c62a6SFranco Fichtner 	case MAN_IR:
30980387638SSascha Wildner 		font[0] = TERMFONT_UNDER;
31080387638SSascha Wildner 		font[1] = TERMFONT_NONE;
31180387638SSascha Wildner 		break;
312070c62a6SFranco Fichtner 	case MAN_IB:
31380387638SSascha Wildner 		font[0] = TERMFONT_UNDER;
31480387638SSascha Wildner 		font[1] = TERMFONT_BOLD;
31580387638SSascha Wildner 		break;
31680387638SSascha Wildner 	default:
31780387638SSascha Wildner 		abort();
31880387638SSascha Wildner 	}
31954ba9607SSascha Wildner 	for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) {
32080387638SSascha Wildner 		term_fontrepl(p, font[i]);
32154ba9607SSascha Wildner 		assert(nn->type == ROFFT_TEXT);
32254ba9607SSascha Wildner 		term_word(p, nn->string);
32354ba9607SSascha Wildner 		if (nn->flags & NODE_EOS)
32454ba9607SSascha Wildner 			p->flags |= TERMP_SENTENCE;
32554ba9607SSascha Wildner 		if (nn->next != NULL)
32680387638SSascha Wildner 			p->flags |= TERMP_NOSPACE;
32780387638SSascha Wildner 	}
32854ba9607SSascha Wildner 	return 0;
32980387638SSascha Wildner }
33080387638SSascha Wildner 
33180387638SSascha Wildner static int
pre_B(DECL_ARGS)33280387638SSascha Wildner pre_B(DECL_ARGS)
33380387638SSascha Wildner {
33480387638SSascha Wildner 	term_fontrepl(p, TERMFONT_BOLD);
33554ba9607SSascha Wildner 	return 1;
33680387638SSascha Wildner }
33780387638SSascha Wildner 
33880387638SSascha Wildner static int
pre_OP(DECL_ARGS)33936342e81SSascha Wildner pre_OP(DECL_ARGS)
34036342e81SSascha Wildner {
34136342e81SSascha Wildner 	term_word(p, "[");
34254ba9607SSascha Wildner 	p->flags |= TERMP_KEEP | TERMP_NOSPACE;
34336342e81SSascha Wildner 
34454ba9607SSascha Wildner 	if ((n = n->child) != NULL) {
34536342e81SSascha Wildner 		term_fontrepl(p, TERMFONT_BOLD);
34636342e81SSascha Wildner 		term_word(p, n->string);
34736342e81SSascha Wildner 	}
34854ba9607SSascha Wildner 	if (n != NULL && n->next != NULL) {
34936342e81SSascha Wildner 		term_fontrepl(p, TERMFONT_UNDER);
35036342e81SSascha Wildner 		term_word(p, n->next->string);
35136342e81SSascha Wildner 	}
35236342e81SSascha Wildner 	term_fontrepl(p, TERMFONT_NONE);
35354ba9607SSascha Wildner 	p->flags &= ~TERMP_KEEP;
35436342e81SSascha Wildner 	p->flags |= TERMP_NOSPACE;
35536342e81SSascha Wildner 	term_word(p, "]");
35654ba9607SSascha Wildner 	return 0;
35780387638SSascha Wildner }
35880387638SSascha Wildner 
35980387638SSascha Wildner static int
pre_in(DECL_ARGS)36080387638SSascha Wildner pre_in(DECL_ARGS)
36180387638SSascha Wildner {
36254ba9607SSascha Wildner 	struct roffsu	 su;
36380387638SSascha Wildner 	const char	*cp;
36454ba9607SSascha Wildner 	size_t		 v;
36554ba9607SSascha Wildner 	int		 less;
36680387638SSascha Wildner 
36780387638SSascha Wildner 	term_newln(p);
36880387638SSascha Wildner 
36954ba9607SSascha Wildner 	if (n->child == NULL) {
37054ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
37154ba9607SSascha Wildner 		return 0;
37280387638SSascha Wildner 	}
37380387638SSascha Wildner 
37480387638SSascha Wildner 	cp = n->child->string;
37580387638SSascha Wildner 	less = 0;
37680387638SSascha Wildner 
37754ba9607SSascha Wildner 	if (*cp == '-')
37880387638SSascha Wildner 		less = -1;
37954ba9607SSascha Wildner 	else if (*cp == '+')
38080387638SSascha Wildner 		less = 1;
38180387638SSascha Wildner 	else
38280387638SSascha Wildner 		cp--;
38380387638SSascha Wildner 
38454ba9607SSascha Wildner 	if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
38554ba9607SSascha Wildner 		return 0;
38680387638SSascha Wildner 
38754ba9607SSascha Wildner 	v = term_hen(p, &su);
38880387638SSascha Wildner 
38980387638SSascha Wildner 	if (less < 0)
39054ba9607SSascha Wildner 		p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
39180387638SSascha Wildner 	else if (less > 0)
39254ba9607SSascha Wildner 		p->tcol->offset += v;
39380387638SSascha Wildner 	else
39454ba9607SSascha Wildner 		p->tcol->offset = v;
39554ba9607SSascha Wildner 	if (p->tcol->offset > SHRT_MAX)
39654ba9607SSascha Wildner 		p->tcol->offset = term_len(p, p->defindent);
39780387638SSascha Wildner 
39854ba9607SSascha Wildner 	return 0;
39980387638SSascha Wildner }
40080387638SSascha Wildner 
40180387638SSascha Wildner static int
pre_DT(DECL_ARGS)40254ba9607SSascha Wildner pre_DT(DECL_ARGS)
40380387638SSascha Wildner {
40454ba9607SSascha Wildner 	term_tab_set(p, NULL);
40554ba9607SSascha Wildner 	term_tab_set(p, "T");
40654ba9607SSascha Wildner 	term_tab_set(p, ".5i");
40754ba9607SSascha Wildner 	return 0;
40880387638SSascha Wildner }
40980387638SSascha Wildner 
41080387638SSascha Wildner static int
pre_HP(DECL_ARGS)41180387638SSascha Wildner pre_HP(DECL_ARGS)
41280387638SSascha Wildner {
41354ba9607SSascha Wildner 	struct roffsu		 su;
41454ba9607SSascha Wildner 	const struct roff_node	*nn;
41554ba9607SSascha Wildner 	int			 len;
41680387638SSascha Wildner 
41780387638SSascha Wildner 	switch (n->type) {
41854ba9607SSascha Wildner 	case ROFFT_BLOCK:
419f88b6c16SFranco Fichtner 		print_bvspace(p, n, mt->pardist);
42054ba9607SSascha Wildner 		return 1;
42154ba9607SSascha Wildner 	case ROFFT_HEAD:
42254ba9607SSascha Wildner 		return 0;
42354ba9607SSascha Wildner 	case ROFFT_BODY:
42480387638SSascha Wildner 		break;
42580387638SSascha Wildner 	default:
42654ba9607SSascha Wildner 		abort();
42780387638SSascha Wildner 	}
42880387638SSascha Wildner 
42954ba9607SSascha Wildner 	if (n->child == NULL)
43054ba9607SSascha Wildner 		return 0;
43154ba9607SSascha Wildner 
43254ba9607SSascha Wildner 	if ((n->child->flags & NODE_NOFILL) == 0) {
433070c62a6SFranco Fichtner 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
4347888c61dSFranco Fichtner 		p->trailspace = 2;
435f88b6c16SFranco Fichtner 	}
436f88b6c16SFranco Fichtner 
43780387638SSascha Wildner 	/* Calculate offset. */
43880387638SSascha Wildner 
43954ba9607SSascha Wildner 	if ((nn = n->parent->head->child) != NULL &&
44054ba9607SSascha Wildner 	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
44154ba9607SSascha Wildner 		len = term_hen(p, &su);
44254ba9607SSascha Wildner 		if (len < 0 && (size_t)(-len) > mt->offset)
44354ba9607SSascha Wildner 			len = -mt->offset;
44454ba9607SSascha Wildner 		else if (len > SHRT_MAX)
44554ba9607SSascha Wildner 			len = term_len(p, p->defindent);
44654ba9607SSascha Wildner 		mt->lmargin[mt->lmargincur] = len;
44754ba9607SSascha Wildner 	} else
44854ba9607SSascha Wildner 		len = mt->lmargin[mt->lmargincur];
44980387638SSascha Wildner 
45054ba9607SSascha Wildner 	p->tcol->offset = mt->offset;
45154ba9607SSascha Wildner 	p->tcol->rmargin = mt->offset + len;
45254ba9607SSascha Wildner 	return 1;
45380387638SSascha Wildner }
45480387638SSascha Wildner 
45580387638SSascha Wildner static void
post_HP(DECL_ARGS)45680387638SSascha Wildner post_HP(DECL_ARGS)
45780387638SSascha Wildner {
45880387638SSascha Wildner 	switch (n->type) {
45954ba9607SSascha Wildner 	case ROFFT_BLOCK:
46054ba9607SSascha Wildner 	case ROFFT_HEAD:
46154ba9607SSascha Wildner 		break;
46254ba9607SSascha Wildner 	case ROFFT_BODY:
463f88b6c16SFranco Fichtner 		term_newln(p);
46454ba9607SSascha Wildner 
46554ba9607SSascha Wildner 		/*
46654ba9607SSascha Wildner 		 * Compatibility with a groff bug.
46754ba9607SSascha Wildner 		 * The .HP macro uses the undocumented .tag request
46854ba9607SSascha Wildner 		 * which causes a line break and cancels no-space
46954ba9607SSascha Wildner 		 * mode even if there isn't any output.
47054ba9607SSascha Wildner 		 */
47154ba9607SSascha Wildner 
47254ba9607SSascha Wildner 		if (n->child == NULL)
47354ba9607SSascha Wildner 			term_vspace(p);
47454ba9607SSascha Wildner 
475070c62a6SFranco Fichtner 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
4767888c61dSFranco Fichtner 		p->trailspace = 0;
47754ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
47854ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
47980387638SSascha Wildner 		break;
48080387638SSascha Wildner 	default:
48154ba9607SSascha Wildner 		abort();
48280387638SSascha Wildner 	}
48380387638SSascha Wildner }
48480387638SSascha Wildner 
48580387638SSascha Wildner static int
pre_PP(DECL_ARGS)48680387638SSascha Wildner pre_PP(DECL_ARGS)
48780387638SSascha Wildner {
48880387638SSascha Wildner 	switch (n->type) {
48954ba9607SSascha Wildner 	case ROFFT_BLOCK:
49036342e81SSascha Wildner 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
491f88b6c16SFranco Fichtner 		print_bvspace(p, n, mt->pardist);
49280387638SSascha Wildner 		break;
49354ba9607SSascha Wildner 	case ROFFT_HEAD:
49454ba9607SSascha Wildner 		return 0;
49554ba9607SSascha Wildner 	case ROFFT_BODY:
49654ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
49780387638SSascha Wildner 		break;
49854ba9607SSascha Wildner 	default:
49954ba9607SSascha Wildner 		abort();
50080387638SSascha Wildner 	}
50154ba9607SSascha Wildner 	return 1;
50280387638SSascha Wildner }
50380387638SSascha Wildner 
50480387638SSascha Wildner static int
pre_IP(DECL_ARGS)50580387638SSascha Wildner pre_IP(DECL_ARGS)
50680387638SSascha Wildner {
50754ba9607SSascha Wildner 	struct roffsu		 su;
50854ba9607SSascha Wildner 	const struct roff_node	*nn;
50954ba9607SSascha Wildner 	int			 len;
51080387638SSascha Wildner 
51180387638SSascha Wildner 	switch (n->type) {
51254ba9607SSascha Wildner 	case ROFFT_BLOCK:
51354ba9607SSascha Wildner 		print_bvspace(p, n, mt->pardist);
51454ba9607SSascha Wildner 		return 1;
51554ba9607SSascha Wildner 	case ROFFT_HEAD:
51680387638SSascha Wildner 		p->flags |= TERMP_NOBREAK;
5177888c61dSFranco Fichtner 		p->trailspace = 1;
51880387638SSascha Wildner 		break;
51954ba9607SSascha Wildner 	case ROFFT_BODY:
52054ba9607SSascha Wildner 		p->flags |= TERMP_NOSPACE;
52154ba9607SSascha Wildner 		break;
52280387638SSascha Wildner 	default:
52354ba9607SSascha Wildner 		abort();
52480387638SSascha Wildner 	}
52580387638SSascha Wildner 
52680387638SSascha Wildner 	/* Calculate the offset from the optional second argument. */
52754ba9607SSascha Wildner 	if ((nn = n->parent->head->child) != NULL &&
52854ba9607SSascha Wildner 	    (nn = nn->next) != NULL &&
52954ba9607SSascha Wildner 	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
53054ba9607SSascha Wildner 		len = term_hen(p, &su);
53154ba9607SSascha Wildner 		if (len < 0 && (size_t)(-len) > mt->offset)
53254ba9607SSascha Wildner 			len = -mt->offset;
53354ba9607SSascha Wildner 		else if (len > SHRT_MAX)
53454ba9607SSascha Wildner 			len = term_len(p, p->defindent);
53554ba9607SSascha Wildner 		mt->lmargin[mt->lmargincur] = len;
53654ba9607SSascha Wildner 	} else
53754ba9607SSascha Wildner 		len = mt->lmargin[mt->lmargincur];
53880387638SSascha Wildner 
53980387638SSascha Wildner 	switch (n->type) {
54054ba9607SSascha Wildner 	case ROFFT_HEAD:
54154ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
54254ba9607SSascha Wildner 		p->tcol->rmargin = mt->offset + len;
54354ba9607SSascha Wildner 		if (n->child != NULL)
544f88b6c16SFranco Fichtner 			print_man_node(p, mt, n->child, meta);
54554ba9607SSascha Wildner 		return 0;
54654ba9607SSascha Wildner 	case ROFFT_BODY:
54754ba9607SSascha Wildner 		p->tcol->offset = mt->offset + len;
54854ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
54980387638SSascha Wildner 		break;
55080387638SSascha Wildner 	default:
55154ba9607SSascha Wildner 		abort();
55280387638SSascha Wildner 	}
55354ba9607SSascha Wildner 	return 1;
55480387638SSascha Wildner }
55580387638SSascha Wildner 
55680387638SSascha Wildner static void
post_IP(DECL_ARGS)55780387638SSascha Wildner post_IP(DECL_ARGS)
55880387638SSascha Wildner {
55980387638SSascha Wildner 	switch (n->type) {
56054ba9607SSascha Wildner 	case ROFFT_BLOCK:
56154ba9607SSascha Wildner 		break;
56254ba9607SSascha Wildner 	case ROFFT_HEAD:
56380387638SSascha Wildner 		term_flushln(p);
56480387638SSascha Wildner 		p->flags &= ~TERMP_NOBREAK;
5657888c61dSFranco Fichtner 		p->trailspace = 0;
56654ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
56780387638SSascha Wildner 		break;
56854ba9607SSascha Wildner 	case ROFFT_BODY:
56980387638SSascha Wildner 		term_newln(p);
57054ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
57180387638SSascha Wildner 		break;
57280387638SSascha Wildner 	default:
57354ba9607SSascha Wildner 		abort();
57480387638SSascha Wildner 	}
57580387638SSascha Wildner }
57680387638SSascha Wildner 
57780387638SSascha Wildner static int
pre_TP(DECL_ARGS)57880387638SSascha Wildner pre_TP(DECL_ARGS)
57980387638SSascha Wildner {
58054ba9607SSascha Wildner 	struct roffsu		 su;
58154ba9607SSascha Wildner 	struct roff_node	*nn;
58254ba9607SSascha Wildner 	int			 len;
58380387638SSascha Wildner 
58480387638SSascha Wildner 	switch (n->type) {
58554ba9607SSascha Wildner 	case ROFFT_BLOCK:
58654ba9607SSascha Wildner 		if (n->tok == MAN_TP)
58754ba9607SSascha Wildner 			print_bvspace(p, n, mt->pardist);
58854ba9607SSascha Wildner 		return 1;
58954ba9607SSascha Wildner 	case ROFFT_HEAD:
59054ba9607SSascha Wildner 		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
5917888c61dSFranco Fichtner 		p->trailspace = 1;
59280387638SSascha Wildner 		break;
59354ba9607SSascha Wildner 	case ROFFT_BODY:
59480387638SSascha Wildner 		p->flags |= TERMP_NOSPACE;
59580387638SSascha Wildner 		break;
59680387638SSascha Wildner 	default:
59754ba9607SSascha Wildner 		abort();
59880387638SSascha Wildner 	}
59980387638SSascha Wildner 
60080387638SSascha Wildner 	/* Calculate offset. */
60180387638SSascha Wildner 
60254ba9607SSascha Wildner 	if ((nn = n->parent->head->child) != NULL &&
60354ba9607SSascha Wildner 	    nn->string != NULL && ! (NODE_LINE & nn->flags) &&
60454ba9607SSascha Wildner 	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
60554ba9607SSascha Wildner 		len = term_hen(p, &su);
60654ba9607SSascha Wildner 		if (len < 0 && (size_t)(-len) > mt->offset)
60754ba9607SSascha Wildner 			len = -mt->offset;
60854ba9607SSascha Wildner 		else if (len > SHRT_MAX)
60954ba9607SSascha Wildner 			len = term_len(p, p->defindent);
61054ba9607SSascha Wildner 		mt->lmargin[mt->lmargincur] = len;
61154ba9607SSascha Wildner 	} else
61254ba9607SSascha Wildner 		len = mt->lmargin[mt->lmargincur];
61380387638SSascha Wildner 
61480387638SSascha Wildner 	switch (n->type) {
61554ba9607SSascha Wildner 	case ROFFT_HEAD:
61654ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
61754ba9607SSascha Wildner 		p->tcol->rmargin = mt->offset + len;
61880387638SSascha Wildner 
61980387638SSascha Wildner 		/* Don't print same-line elements. */
620070c62a6SFranco Fichtner 		nn = n->child;
62154ba9607SSascha Wildner 		while (nn != NULL && (nn->flags & NODE_LINE) == 0)
622070c62a6SFranco Fichtner 			nn = nn->next;
623070c62a6SFranco Fichtner 
62454ba9607SSascha Wildner 		while (nn != NULL) {
625f88b6c16SFranco Fichtner 			print_man_node(p, mt, nn, meta);
626070c62a6SFranco Fichtner 			nn = nn->next;
627070c62a6SFranco Fichtner 		}
62854ba9607SSascha Wildner 		return 0;
62954ba9607SSascha Wildner 	case ROFFT_BODY:
63054ba9607SSascha Wildner 		p->tcol->offset = mt->offset + len;
63154ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
6327888c61dSFranco Fichtner 		p->trailspace = 0;
63354ba9607SSascha Wildner 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
63480387638SSascha Wildner 		break;
63580387638SSascha Wildner 	default:
63654ba9607SSascha Wildner 		abort();
63780387638SSascha Wildner 	}
63854ba9607SSascha Wildner 	return 1;
63980387638SSascha Wildner }
64080387638SSascha Wildner 
64180387638SSascha Wildner static void
post_TP(DECL_ARGS)64280387638SSascha Wildner post_TP(DECL_ARGS)
64380387638SSascha Wildner {
64480387638SSascha Wildner 	switch (n->type) {
64554ba9607SSascha Wildner 	case ROFFT_BLOCK:
64654ba9607SSascha Wildner 		break;
64754ba9607SSascha Wildner 	case ROFFT_HEAD:
64880387638SSascha Wildner 		term_flushln(p);
64980387638SSascha Wildner 		break;
65054ba9607SSascha Wildner 	case ROFFT_BODY:
65180387638SSascha Wildner 		term_newln(p);
65254ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
65380387638SSascha Wildner 		break;
65480387638SSascha Wildner 	default:
65554ba9607SSascha Wildner 		abort();
65680387638SSascha Wildner 	}
65780387638SSascha Wildner }
65880387638SSascha Wildner 
65980387638SSascha Wildner static int
pre_SS(DECL_ARGS)66080387638SSascha Wildner pre_SS(DECL_ARGS)
66180387638SSascha Wildner {
662f88b6c16SFranco Fichtner 	int	 i;
66380387638SSascha Wildner 
66480387638SSascha Wildner 	switch (n->type) {
66554ba9607SSascha Wildner 	case ROFFT_BLOCK:
66636342e81SSascha Wildner 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
66736342e81SSascha Wildner 		mt->offset = term_len(p, p->defindent);
66854ba9607SSascha Wildner 
66954ba9607SSascha Wildner 		/*
67054ba9607SSascha Wildner 		 * No vertical space before the first subsection
67154ba9607SSascha Wildner 		 * and after an empty subsection.
67254ba9607SSascha Wildner 		 */
67354ba9607SSascha Wildner 
674*99db7d0eSSascha Wildner 		if ((n = roff_node_prev(n)) == NULL ||
675*99db7d0eSSascha Wildner 		    (n->tok == MAN_SS && roff_node_child(n->body) == NULL))
67680387638SSascha Wildner 			break;
67754ba9607SSascha Wildner 
678f88b6c16SFranco Fichtner 		for (i = 0; i < mt->pardist; i++)
67980387638SSascha Wildner 			term_vspace(p);
68080387638SSascha Wildner 		break;
68154ba9607SSascha Wildner 	case ROFFT_HEAD:
68280387638SSascha Wildner 		term_fontrepl(p, TERMFONT_BOLD);
68354ba9607SSascha Wildner 		p->tcol->offset = term_len(p, 3);
68454ba9607SSascha Wildner 		p->tcol->rmargin = mt->offset;
68554ba9607SSascha Wildner 		p->trailspace = mt->offset;
68654ba9607SSascha Wildner 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
68780387638SSascha Wildner 		break;
68854ba9607SSascha Wildner 	case ROFFT_BODY:
68954ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
69054ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
69154ba9607SSascha Wildner 		p->trailspace = 0;
69254ba9607SSascha Wildner 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
69380387638SSascha Wildner 		break;
69480387638SSascha Wildner 	default:
69580387638SSascha Wildner 		break;
69680387638SSascha Wildner 	}
69754ba9607SSascha Wildner 	return 1;
69880387638SSascha Wildner }
69980387638SSascha Wildner 
70080387638SSascha Wildner static int
pre_SH(DECL_ARGS)70180387638SSascha Wildner pre_SH(DECL_ARGS)
70280387638SSascha Wildner {
703f88b6c16SFranco Fichtner 	int	 i;
70480387638SSascha Wildner 
70580387638SSascha Wildner 	switch (n->type) {
70654ba9607SSascha Wildner 	case ROFFT_BLOCK:
70736342e81SSascha Wildner 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
70836342e81SSascha Wildner 		mt->offset = term_len(p, p->defindent);
70954ba9607SSascha Wildner 
71054ba9607SSascha Wildner 		/*
71154ba9607SSascha Wildner 		 * No vertical space before the first section
71254ba9607SSascha Wildner 		 * and after an empty section.
71354ba9607SSascha Wildner 		 */
71454ba9607SSascha Wildner 
715*99db7d0eSSascha Wildner 		if ((n = roff_node_prev(n)) == NULL ||
716*99db7d0eSSascha Wildner 		    (n->tok == MAN_SH && roff_node_child(n->body) == NULL))
71780387638SSascha Wildner 			break;
71854ba9607SSascha Wildner 
719f88b6c16SFranco Fichtner 		for (i = 0; i < mt->pardist; i++)
72080387638SSascha Wildner 			term_vspace(p);
72180387638SSascha Wildner 		break;
72254ba9607SSascha Wildner 	case ROFFT_HEAD:
72380387638SSascha Wildner 		term_fontrepl(p, TERMFONT_BOLD);
72454ba9607SSascha Wildner 		p->tcol->offset = 0;
72554ba9607SSascha Wildner 		p->tcol->rmargin = mt->offset;
72654ba9607SSascha Wildner 		p->trailspace = mt->offset;
72754ba9607SSascha Wildner 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
72880387638SSascha Wildner 		break;
72954ba9607SSascha Wildner 	case ROFFT_BODY:
73054ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
73154ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
73254ba9607SSascha Wildner 		p->trailspace = 0;
73354ba9607SSascha Wildner 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
73480387638SSascha Wildner 		break;
73580387638SSascha Wildner 	default:
73654ba9607SSascha Wildner 		abort();
73780387638SSascha Wildner 	}
73854ba9607SSascha Wildner 	return 1;
73980387638SSascha Wildner }
74080387638SSascha Wildner 
74180387638SSascha Wildner static void
post_SH(DECL_ARGS)74280387638SSascha Wildner post_SH(DECL_ARGS)
74380387638SSascha Wildner {
74480387638SSascha Wildner 	switch (n->type) {
74554ba9607SSascha Wildner 	case ROFFT_BLOCK:
74680387638SSascha Wildner 		break;
74754ba9607SSascha Wildner 	case ROFFT_HEAD:
74854ba9607SSascha Wildner 	case ROFFT_BODY:
74980387638SSascha Wildner 		term_newln(p);
75080387638SSascha Wildner 		break;
75180387638SSascha Wildner 	default:
75254ba9607SSascha Wildner 		abort();
75380387638SSascha Wildner 	}
75480387638SSascha Wildner }
75580387638SSascha Wildner 
75680387638SSascha Wildner static int
pre_RS(DECL_ARGS)75780387638SSascha Wildner pre_RS(DECL_ARGS)
75880387638SSascha Wildner {
75954ba9607SSascha Wildner 	struct roffsu	 su;
76080387638SSascha Wildner 
76180387638SSascha Wildner 	switch (n->type) {
76254ba9607SSascha Wildner 	case ROFFT_BLOCK:
76380387638SSascha Wildner 		term_newln(p);
76454ba9607SSascha Wildner 		return 1;
76554ba9607SSascha Wildner 	case ROFFT_HEAD:
76654ba9607SSascha Wildner 		return 0;
76754ba9607SSascha Wildner 	case ROFFT_BODY:
76880387638SSascha Wildner 		break;
76954ba9607SSascha Wildner 	default:
77054ba9607SSascha Wildner 		abort();
77180387638SSascha Wildner 	}
77280387638SSascha Wildner 
77354ba9607SSascha Wildner 	n = n->parent->head;
77454ba9607SSascha Wildner 	n->aux = SHRT_MAX + 1;
77554ba9607SSascha Wildner 	if (n->child == NULL)
77654ba9607SSascha Wildner 		n->aux = mt->lmargin[mt->lmargincur];
77754ba9607SSascha Wildner 	else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
77854ba9607SSascha Wildner 		n->aux = term_hen(p, &su);
77954ba9607SSascha Wildner 	if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
78054ba9607SSascha Wildner 		n->aux = -mt->offset;
78154ba9607SSascha Wildner 	else if (n->aux > SHRT_MAX)
78254ba9607SSascha Wildner 		n->aux = term_len(p, p->defindent);
78336342e81SSascha Wildner 
78454ba9607SSascha Wildner 	mt->offset += n->aux;
78554ba9607SSascha Wildner 	p->tcol->offset = mt->offset;
78654ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin;
78736342e81SSascha Wildner 
78836342e81SSascha Wildner 	if (++mt->lmarginsz < MAXMARGINS)
78936342e81SSascha Wildner 		mt->lmargincur = mt->lmarginsz;
79036342e81SSascha Wildner 
79154ba9607SSascha Wildner 	mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
79254ba9607SSascha Wildner 	return 1;
79380387638SSascha Wildner }
79480387638SSascha Wildner 
79580387638SSascha Wildner static void
post_RS(DECL_ARGS)79680387638SSascha Wildner post_RS(DECL_ARGS)
79780387638SSascha Wildner {
79880387638SSascha Wildner 	switch (n->type) {
79954ba9607SSascha Wildner 	case ROFFT_BLOCK:
80054ba9607SSascha Wildner 	case ROFFT_HEAD:
80136342e81SSascha Wildner 		return;
80254ba9607SSascha Wildner 	case ROFFT_BODY:
80380387638SSascha Wildner 		break;
80454ba9607SSascha Wildner 	default:
80554ba9607SSascha Wildner 		abort();
80680387638SSascha Wildner 	}
80754ba9607SSascha Wildner 	term_newln(p);
80854ba9607SSascha Wildner 	mt->offset -= n->parent->head->aux;
80954ba9607SSascha Wildner 	p->tcol->offset = mt->offset;
81036342e81SSascha Wildner 	if (--mt->lmarginsz < MAXMARGINS)
81136342e81SSascha Wildner 		mt->lmargincur = mt->lmarginsz;
81236342e81SSascha Wildner }
81380387638SSascha Wildner 
8147888c61dSFranco Fichtner static int
pre_SY(DECL_ARGS)81554ba9607SSascha Wildner pre_SY(DECL_ARGS)
81654ba9607SSascha Wildner {
81754ba9607SSascha Wildner 	const struct roff_node	*nn;
81854ba9607SSascha Wildner 	int			 len;
81954ba9607SSascha Wildner 
82054ba9607SSascha Wildner 	switch (n->type) {
82154ba9607SSascha Wildner 	case ROFFT_BLOCK:
822*99db7d0eSSascha Wildner 		if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY)
82354ba9607SSascha Wildner 			print_bvspace(p, n, mt->pardist);
82454ba9607SSascha Wildner 		return 1;
82554ba9607SSascha Wildner 	case ROFFT_HEAD:
82654ba9607SSascha Wildner 	case ROFFT_BODY:
82754ba9607SSascha Wildner 		break;
82854ba9607SSascha Wildner 	default:
82954ba9607SSascha Wildner 		abort();
83054ba9607SSascha Wildner 	}
83154ba9607SSascha Wildner 
83254ba9607SSascha Wildner 	nn = n->parent->head->child;
83354ba9607SSascha Wildner 	len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
83454ba9607SSascha Wildner 
83554ba9607SSascha Wildner 	switch (n->type) {
83654ba9607SSascha Wildner 	case ROFFT_HEAD:
83754ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
83854ba9607SSascha Wildner 		p->tcol->rmargin = mt->offset + len;
83954ba9607SSascha Wildner 		if (n->next->child == NULL ||
84054ba9607SSascha Wildner 		    (n->next->child->flags & NODE_NOFILL) == 0)
84154ba9607SSascha Wildner 			p->flags |= TERMP_NOBREAK;
84254ba9607SSascha Wildner 		term_fontrepl(p, TERMFONT_BOLD);
84354ba9607SSascha Wildner 		break;
84454ba9607SSascha Wildner 	case ROFFT_BODY:
84554ba9607SSascha Wildner 		mt->lmargin[mt->lmargincur] = len;
84654ba9607SSascha Wildner 		p->tcol->offset = mt->offset + len;
84754ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
84854ba9607SSascha Wildner 		p->flags |= TERMP_NOSPACE;
84954ba9607SSascha Wildner 		break;
85054ba9607SSascha Wildner 	default:
85154ba9607SSascha Wildner 		abort();
85254ba9607SSascha Wildner 	}
85354ba9607SSascha Wildner 	return 1;
85454ba9607SSascha Wildner }
85554ba9607SSascha Wildner 
85654ba9607SSascha Wildner static void
post_SY(DECL_ARGS)85754ba9607SSascha Wildner post_SY(DECL_ARGS)
85854ba9607SSascha Wildner {
85954ba9607SSascha Wildner 	switch (n->type) {
86054ba9607SSascha Wildner 	case ROFFT_BLOCK:
86154ba9607SSascha Wildner 		break;
86254ba9607SSascha Wildner 	case ROFFT_HEAD:
86354ba9607SSascha Wildner 		term_flushln(p);
86454ba9607SSascha Wildner 		p->flags &= ~TERMP_NOBREAK;
86554ba9607SSascha Wildner 		break;
86654ba9607SSascha Wildner 	case ROFFT_BODY:
86754ba9607SSascha Wildner 		term_newln(p);
86854ba9607SSascha Wildner 		p->tcol->offset = mt->offset;
86954ba9607SSascha Wildner 		break;
87054ba9607SSascha Wildner 	default:
87154ba9607SSascha Wildner 		abort();
87254ba9607SSascha Wildner 	}
87354ba9607SSascha Wildner }
87454ba9607SSascha Wildner 
87554ba9607SSascha Wildner static int
pre_UR(DECL_ARGS)8767888c61dSFranco Fichtner pre_UR(DECL_ARGS)
8777888c61dSFranco Fichtner {
87854ba9607SSascha Wildner 	return n->type != ROFFT_HEAD;
8797888c61dSFranco Fichtner }
8807888c61dSFranco Fichtner 
8817888c61dSFranco Fichtner static void
post_UR(DECL_ARGS)8827888c61dSFranco Fichtner post_UR(DECL_ARGS)
8837888c61dSFranco Fichtner {
88454ba9607SSascha Wildner 	if (n->type != ROFFT_BLOCK)
8857888c61dSFranco Fichtner 		return;
8867888c61dSFranco Fichtner 
8877888c61dSFranco Fichtner 	term_word(p, "<");
8887888c61dSFranco Fichtner 	p->flags |= TERMP_NOSPACE;
8897888c61dSFranco Fichtner 
89054ba9607SSascha Wildner 	if (n->child->child != NULL)
8917888c61dSFranco Fichtner 		print_man_node(p, mt, n->child->child, meta);
8927888c61dSFranco Fichtner 
8937888c61dSFranco Fichtner 	p->flags |= TERMP_NOSPACE;
8947888c61dSFranco Fichtner 	term_word(p, ">");
8957888c61dSFranco Fichtner }
8967888c61dSFranco Fichtner 
89780387638SSascha Wildner static void
print_man_node(DECL_ARGS)89880387638SSascha Wildner print_man_node(DECL_ARGS)
89980387638SSascha Wildner {
90054ba9607SSascha Wildner 	const struct man_term_act *act;
90180387638SSascha Wildner 	int c;
90280387638SSascha Wildner 
903*99db7d0eSSascha Wildner 	if (n->flags & NODE_ID)
904*99db7d0eSSascha Wildner 		term_tag_write(n, p->line);
905*99db7d0eSSascha Wildner 
90680387638SSascha Wildner 	switch (n->type) {
90754ba9607SSascha Wildner 	case ROFFT_TEXT:
90860e1e752SSascha Wildner 		/*
90960e1e752SSascha Wildner 		 * If we have a blank line, output a vertical space.
91060e1e752SSascha Wildner 		 * If we have a space as the first character, break
91160e1e752SSascha Wildner 		 * before printing the line's data.
91260e1e752SSascha Wildner 		 */
91354ba9607SSascha Wildner 		if (*n->string == '\0') {
91454ba9607SSascha Wildner 			if (p->flags & TERMP_NONEWLINE)
91554ba9607SSascha Wildner 				term_newln(p);
91654ba9607SSascha Wildner 			else
91780387638SSascha Wildner 				term_vspace(p);
91860e1e752SSascha Wildner 			return;
91954ba9607SSascha Wildner 		} else if (*n->string == ' ' && n->flags & NODE_LINE &&
92054ba9607SSascha Wildner 		    (p->flags & TERMP_NONEWLINE) == 0)
92160e1e752SSascha Wildner 			term_newln(p);
92254ba9607SSascha Wildner 		else if (n->flags & NODE_DELIMC)
92354ba9607SSascha Wildner 			p->flags |= TERMP_NOSPACE;
92480387638SSascha Wildner 
92580387638SSascha Wildner 		term_word(p, n->string);
926f88b6c16SFranco Fichtner 		goto out;
92754ba9607SSascha Wildner 	case ROFFT_COMMENT:
92860e1e752SSascha Wildner 		return;
92954ba9607SSascha Wildner 	case ROFFT_EQN:
93054ba9607SSascha Wildner 		if ( ! (n->flags & NODE_LINE))
93154ba9607SSascha Wildner 			p->flags |= TERMP_NOSPACE;
93254ba9607SSascha Wildner 		term_eqn(p, n->eqn);
93354ba9607SSascha Wildner 		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
93454ba9607SSascha Wildner 			p->flags |= TERMP_NOSPACE;
93554ba9607SSascha Wildner 		return;
93654ba9607SSascha Wildner 	case ROFFT_TBL:
93754ba9607SSascha Wildner 		if (p->tbl.cols == NULL)
93854ba9607SSascha Wildner 			term_vspace(p);
93980387638SSascha Wildner 		term_tbl(p, n->span);
94060e1e752SSascha Wildner 		return;
94180387638SSascha Wildner 	default:
94280387638SSascha Wildner 		break;
94380387638SSascha Wildner 	}
94480387638SSascha Wildner 
94554ba9607SSascha Wildner 	if (n->tok < ROFF_MAX) {
94654ba9607SSascha Wildner 		roff_term_pre(p, n);
94754ba9607SSascha Wildner 		return;
94854ba9607SSascha Wildner 	}
94954ba9607SSascha Wildner 
95054ba9607SSascha Wildner 	act = man_term_act(n->tok);
95154ba9607SSascha Wildner 	if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
95260e1e752SSascha Wildner 		term_fontrepl(p, TERMFONT_NONE);
95360e1e752SSascha Wildner 
95460e1e752SSascha Wildner 	c = 1;
95554ba9607SSascha Wildner 	if (act->pre != NULL)
95654ba9607SSascha Wildner 		c = (*act->pre)(p, mt, n, meta);
95760e1e752SSascha Wildner 
95854ba9607SSascha Wildner 	if (c && n->child != NULL)
959f88b6c16SFranco Fichtner 		print_man_nodelist(p, mt, n->child, meta);
96080387638SSascha Wildner 
96154ba9607SSascha Wildner 	if (act->post != NULL)
96254ba9607SSascha Wildner 		(*act->post)(p, mt, n, meta);
96354ba9607SSascha Wildner 	if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
96480387638SSascha Wildner 		term_fontrepl(p, TERMFONT_NONE);
96580387638SSascha Wildner 
966f88b6c16SFranco Fichtner out:
967f88b6c16SFranco Fichtner 	/*
968f88b6c16SFranco Fichtner 	 * If we're in a literal context, make sure that words
969f88b6c16SFranco Fichtner 	 * together on the same line stay together.  This is a
970f88b6c16SFranco Fichtner 	 * POST-printing call, so we check the NEXT word.  Since
971f88b6c16SFranco Fichtner 	 * -man doesn't have nested macros, we don't need to be
972f88b6c16SFranco Fichtner 	 * more specific than this.
973f88b6c16SFranco Fichtner 	 */
97454ba9607SSascha Wildner 	if (n->flags & NODE_NOFILL &&
97554ba9607SSascha Wildner 	    ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
97654ba9607SSascha Wildner 	    (n->next == NULL || n->next->flags & NODE_LINE)) {
97754ba9607SSascha Wildner 		p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
97854ba9607SSascha Wildner 		if (n->string != NULL && *n->string != '\0')
979f88b6c16SFranco Fichtner 			term_flushln(p);
980f88b6c16SFranco Fichtner 		else
981f88b6c16SFranco Fichtner 			term_newln(p);
98254ba9607SSascha Wildner 		p->flags &= ~TERMP_BRNEVER;
98354ba9607SSascha Wildner 		if (p->tcol->rmargin < p->maxrmargin &&
98454ba9607SSascha Wildner 		    n->parent->tok == MAN_HP) {
98554ba9607SSascha Wildner 			p->tcol->offset = p->tcol->rmargin;
98654ba9607SSascha Wildner 			p->tcol->rmargin = p->maxrmargin;
987f88b6c16SFranco Fichtner 		}
98854ba9607SSascha Wildner 	}
98954ba9607SSascha Wildner 	if (n->flags & NODE_EOS)
99080387638SSascha Wildner 		p->flags |= TERMP_SENTENCE;
99180387638SSascha Wildner }
99280387638SSascha Wildner 
99380387638SSascha Wildner static void
print_man_nodelist(DECL_ARGS)99480387638SSascha Wildner print_man_nodelist(DECL_ARGS)
99580387638SSascha Wildner {
99654ba9607SSascha Wildner 	while (n != NULL) {
997f88b6c16SFranco Fichtner 		print_man_node(p, mt, n, meta);
99854ba9607SSascha Wildner 		n = n->next;
99954ba9607SSascha Wildner 	}
100080387638SSascha Wildner }
100180387638SSascha Wildner 
100280387638SSascha Wildner static void
print_man_foot(struct termp * p,const struct roff_meta * meta)100354ba9607SSascha Wildner print_man_foot(struct termp *p, const struct roff_meta *meta)
100480387638SSascha Wildner {
1005070c62a6SFranco Fichtner 	char			*title;
100654ba9607SSascha Wildner 	size_t			 datelen, titlen;
100780387638SSascha Wildner 
100836342e81SSascha Wildner 	assert(meta->title);
100936342e81SSascha Wildner 	assert(meta->msec);
101036342e81SSascha Wildner 	assert(meta->date);
101180387638SSascha Wildner 
101280387638SSascha Wildner 	term_fontrepl(p, TERMFONT_NONE);
101380387638SSascha Wildner 
1014070c62a6SFranco Fichtner 	if (meta->hasbody)
101580387638SSascha Wildner 		term_vspace(p);
101636342e81SSascha Wildner 
101736342e81SSascha Wildner 	/*
101836342e81SSascha Wildner 	 * Temporary, undocumented option to imitate mdoc(7) output.
101954ba9607SSascha Wildner 	 * In the bottom right corner, use the operating system
102054ba9607SSascha Wildner 	 * instead of the title.
102136342e81SSascha Wildner 	 */
102236342e81SSascha Wildner 
102336342e81SSascha Wildner 	if ( ! p->mdocstyle) {
1024070c62a6SFranco Fichtner 		mandoc_asprintf(&title, "%s(%s)",
1025070c62a6SFranco Fichtner 		    meta->title, meta->msec);
102654ba9607SSascha Wildner 	} else if (meta->os != NULL) {
102754ba9607SSascha Wildner 		title = mandoc_strdup(meta->os);
102836342e81SSascha Wildner 	} else {
1029070c62a6SFranco Fichtner 		title = mandoc_strdup("");
103036342e81SSascha Wildner 	}
103136342e81SSascha Wildner 	datelen = term_strlen(p, meta->date);
103236342e81SSascha Wildner 
103354ba9607SSascha Wildner 	/* Bottom left corner: operating system. */
103480387638SSascha Wildner 
103580387638SSascha Wildner 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
10367888c61dSFranco Fichtner 	p->trailspace = 1;
103754ba9607SSascha Wildner 	p->tcol->offset = 0;
103854ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin > datelen ?
103954ba9607SSascha Wildner 	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
104080387638SSascha Wildner 
104154ba9607SSascha Wildner 	if (meta->os)
104254ba9607SSascha Wildner 		term_word(p, meta->os);
104380387638SSascha Wildner 	term_flushln(p);
104480387638SSascha Wildner 
104536342e81SSascha Wildner 	/* At the bottom in the middle: manual date. */
104636342e81SSascha Wildner 
104754ba9607SSascha Wildner 	p->tcol->offset = p->tcol->rmargin;
104854ba9607SSascha Wildner 	titlen = term_strlen(p, title);
104954ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin > titlen ?
105054ba9607SSascha Wildner 	    p->maxrmargin - titlen : 0;
105136342e81SSascha Wildner 	p->flags |= TERMP_NOSPACE;
105280387638SSascha Wildner 
105360e1e752SSascha Wildner 	term_word(p, meta->date);
105480387638SSascha Wildner 	term_flushln(p);
105536342e81SSascha Wildner 
105636342e81SSascha Wildner 	/* Bottom right corner: manual title and section. */
105736342e81SSascha Wildner 
105836342e81SSascha Wildner 	p->flags &= ~TERMP_NOBREAK;
105936342e81SSascha Wildner 	p->flags |= TERMP_NOSPACE;
10607888c61dSFranco Fichtner 	p->trailspace = 0;
106154ba9607SSascha Wildner 	p->tcol->offset = p->tcol->rmargin;
106254ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin;
106336342e81SSascha Wildner 
106436342e81SSascha Wildner 	term_word(p, title);
106536342e81SSascha Wildner 	term_flushln(p);
106654ba9607SSascha Wildner 
106754ba9607SSascha Wildner 	/*
106854ba9607SSascha Wildner 	 * Reset the terminal state for more output after the footer:
106954ba9607SSascha Wildner 	 * Some output modes, in particular PostScript and PDF, print
107054ba9607SSascha Wildner 	 * the header and the footer into a buffer such that it can be
107154ba9607SSascha Wildner 	 * reused for multiple output pages, then go on to format the
107254ba9607SSascha Wildner 	 * main text.
107354ba9607SSascha Wildner 	 */
107454ba9607SSascha Wildner 
107554ba9607SSascha Wildner         p->tcol->offset = 0;
107654ba9607SSascha Wildner         p->flags = 0;
107754ba9607SSascha Wildner 
1078070c62a6SFranco Fichtner 	free(title);
107980387638SSascha Wildner }
108080387638SSascha Wildner 
108180387638SSascha Wildner static void
print_man_head(struct termp * p,const struct roff_meta * meta)108254ba9607SSascha Wildner print_man_head(struct termp *p, const struct roff_meta *meta)
108380387638SSascha Wildner {
1084070c62a6SFranco Fichtner 	const char		*volume;
1085070c62a6SFranco Fichtner 	char			*title;
1086070c62a6SFranco Fichtner 	size_t			 vollen, titlen;
108780387638SSascha Wildner 
1088f88b6c16SFranco Fichtner 	assert(meta->title);
1089f88b6c16SFranco Fichtner 	assert(meta->msec);
109080387638SSascha Wildner 
1091070c62a6SFranco Fichtner 	volume = NULL == meta->vol ? "" : meta->vol;
1092070c62a6SFranco Fichtner 	vollen = term_strlen(p, volume);
109380387638SSascha Wildner 
109436342e81SSascha Wildner 	/* Top left corner: manual title and section. */
109536342e81SSascha Wildner 
1096070c62a6SFranco Fichtner 	mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
109780387638SSascha Wildner 	titlen = term_strlen(p, title);
109880387638SSascha Wildner 
109936342e81SSascha Wildner 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
11007888c61dSFranco Fichtner 	p->trailspace = 1;
110154ba9607SSascha Wildner 	p->tcol->offset = 0;
110254ba9607SSascha Wildner 	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1103070c62a6SFranco Fichtner 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
110454ba9607SSascha Wildner 	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
110580387638SSascha Wildner 
110680387638SSascha Wildner 	term_word(p, title);
110780387638SSascha Wildner 	term_flushln(p);
110880387638SSascha Wildner 
110936342e81SSascha Wildner 	/* At the top in the middle: manual volume. */
111036342e81SSascha Wildner 
111136342e81SSascha Wildner 	p->flags |= TERMP_NOSPACE;
111254ba9607SSascha Wildner 	p->tcol->offset = p->tcol->rmargin;
111354ba9607SSascha Wildner 	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
111454ba9607SSascha Wildner 	    p->maxrmargin ?  p->maxrmargin - titlen : p->maxrmargin;
111580387638SSascha Wildner 
1116070c62a6SFranco Fichtner 	term_word(p, volume);
111780387638SSascha Wildner 	term_flushln(p);
111880387638SSascha Wildner 
111936342e81SSascha Wildner 	/* Top right corner: title and section, again. */
112036342e81SSascha Wildner 
112180387638SSascha Wildner 	p->flags &= ~TERMP_NOBREAK;
11227888c61dSFranco Fichtner 	p->trailspace = 0;
112354ba9607SSascha Wildner 	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
112436342e81SSascha Wildner 		p->flags |= TERMP_NOSPACE;
112554ba9607SSascha Wildner 		p->tcol->offset = p->tcol->rmargin;
112654ba9607SSascha Wildner 		p->tcol->rmargin = p->maxrmargin;
112780387638SSascha Wildner 		term_word(p, title);
112880387638SSascha Wildner 		term_flushln(p);
112980387638SSascha Wildner 	}
113080387638SSascha Wildner 
113180387638SSascha Wildner 	p->flags &= ~TERMP_NOSPACE;
113254ba9607SSascha Wildner 	p->tcol->offset = 0;
113354ba9607SSascha Wildner 	p->tcol->rmargin = p->maxrmargin;
113480387638SSascha Wildner 
113580387638SSascha Wildner 	/*
113636342e81SSascha Wildner 	 * Groff prints three blank lines before the content.
113736342e81SSascha Wildner 	 * Do the same, except in the temporary, undocumented
113836342e81SSascha Wildner 	 * mode imitating mdoc(7) output.
113980387638SSascha Wildner 	 */
114080387638SSascha Wildner 
114180387638SSascha Wildner 	term_vspace(p);
1142070c62a6SFranco Fichtner 	free(title);
114336342e81SSascha Wildner }
1144