xref: /illumos-gate/usr/src/cmd/mandoc/man_term.c (revision 371584c2eae4cf827fd406ba26c14f021adaaa70)
1*371584c2SYuri Pankov /*	$Id: man_term.c,v 1.187 2016/01/08 17:48:09 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4260e9a87SYuri Pankov  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
10*371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
1995c635efSGarrett D'Amore 
2095c635efSGarrett D'Amore #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <ctype.h>
24260e9a87SYuri Pankov #include <limits.h>
2595c635efSGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <stdlib.h>
2795c635efSGarrett D'Amore #include <string.h>
2895c635efSGarrett D'Amore 
29260e9a87SYuri Pankov #include "mandoc_aux.h"
30*371584c2SYuri Pankov #include "mandoc.h"
31*371584c2SYuri Pankov #include "roff.h"
3295c635efSGarrett D'Amore #include "man.h"
33*371584c2SYuri Pankov #include "out.h"
3495c635efSGarrett D'Amore #include "term.h"
3595c635efSGarrett D'Amore #include "main.h"
3695c635efSGarrett D'Amore 
3795c635efSGarrett D'Amore #define	MAXMARGINS	  64 /* maximum number of indented scopes */
3895c635efSGarrett D'Amore 
3995c635efSGarrett D'Amore struct	mtermp {
4095c635efSGarrett D'Amore 	int		  fl;
4195c635efSGarrett D'Amore #define	MANT_LITERAL	 (1 << 0)
42260e9a87SYuri Pankov 	int		  lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
4395c635efSGarrett D'Amore 	int		  lmargincur; /* index of current margin */
4495c635efSGarrett D'Amore 	int		  lmarginsz; /* actual number of nested margins */
4595c635efSGarrett D'Amore 	size_t		  offset; /* default offset to visible page */
46698f87a4SGarrett D'Amore 	int		  pardist; /* vert. space before par., unit: [v] */
4795c635efSGarrett D'Amore };
4895c635efSGarrett D'Amore 
4995c635efSGarrett D'Amore #define	DECL_ARGS	  struct termp *p, \
5095c635efSGarrett D'Amore 			  struct mtermp *mt, \
51*371584c2SYuri Pankov 			  struct roff_node *n, \
52*371584c2SYuri Pankov 			  const struct roff_meta *meta
5395c635efSGarrett D'Amore 
5495c635efSGarrett D'Amore struct	termact {
5595c635efSGarrett D'Amore 	int		(*pre)(DECL_ARGS);
5695c635efSGarrett D'Amore 	void		(*post)(DECL_ARGS);
5795c635efSGarrett D'Amore 	int		  flags;
5895c635efSGarrett D'Amore #define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
5995c635efSGarrett D'Amore };
6095c635efSGarrett D'Amore 
6195c635efSGarrett D'Amore static	void		  print_man_nodelist(DECL_ARGS);
6295c635efSGarrett D'Amore static	void		  print_man_node(DECL_ARGS);
63*371584c2SYuri Pankov static	void		  print_man_head(struct termp *,
64*371584c2SYuri Pankov 				const struct roff_meta *);
65*371584c2SYuri Pankov static	void		  print_man_foot(struct termp *,
66*371584c2SYuri Pankov 				const struct roff_meta *);
6795c635efSGarrett D'Amore static	void		  print_bvspace(struct termp *,
68*371584c2SYuri Pankov 				const struct roff_node *, int);
6995c635efSGarrett D'Amore 
7095c635efSGarrett D'Amore static	int		  pre_B(DECL_ARGS);
7195c635efSGarrett D'Amore static	int		  pre_HP(DECL_ARGS);
7295c635efSGarrett D'Amore static	int		  pre_I(DECL_ARGS);
7395c635efSGarrett D'Amore static	int		  pre_IP(DECL_ARGS);
7495c635efSGarrett D'Amore static	int		  pre_OP(DECL_ARGS);
75698f87a4SGarrett D'Amore static	int		  pre_PD(DECL_ARGS);
7695c635efSGarrett D'Amore static	int		  pre_PP(DECL_ARGS);
7795c635efSGarrett D'Amore static	int		  pre_RS(DECL_ARGS);
7895c635efSGarrett D'Amore static	int		  pre_SH(DECL_ARGS);
7995c635efSGarrett D'Amore static	int		  pre_SS(DECL_ARGS);
8095c635efSGarrett D'Amore static	int		  pre_TP(DECL_ARGS);
81698f87a4SGarrett D'Amore static	int		  pre_UR(DECL_ARGS);
8295c635efSGarrett D'Amore static	int		  pre_alternate(DECL_ARGS);
8395c635efSGarrett D'Amore static	int		  pre_ft(DECL_ARGS);
8495c635efSGarrett D'Amore static	int		  pre_ign(DECL_ARGS);
8595c635efSGarrett D'Amore static	int		  pre_in(DECL_ARGS);
8695c635efSGarrett D'Amore static	int		  pre_literal(DECL_ARGS);
87260e9a87SYuri Pankov static	int		  pre_ll(DECL_ARGS);
8895c635efSGarrett D'Amore static	int		  pre_sp(DECL_ARGS);
8995c635efSGarrett D'Amore 
9095c635efSGarrett D'Amore static	void		  post_IP(DECL_ARGS);
9195c635efSGarrett D'Amore static	void		  post_HP(DECL_ARGS);
9295c635efSGarrett D'Amore static	void		  post_RS(DECL_ARGS);
9395c635efSGarrett D'Amore static	void		  post_SH(DECL_ARGS);
9495c635efSGarrett D'Amore static	void		  post_SS(DECL_ARGS);
9595c635efSGarrett D'Amore static	void		  post_TP(DECL_ARGS);
96698f87a4SGarrett D'Amore static	void		  post_UR(DECL_ARGS);
9795c635efSGarrett D'Amore 
9895c635efSGarrett D'Amore static	const struct termact termacts[MAN_MAX] = {
9995c635efSGarrett D'Amore 	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
10095c635efSGarrett D'Amore 	{ NULL, NULL, 0 }, /* TH */
10195c635efSGarrett D'Amore 	{ pre_SH, post_SH, 0 }, /* SH */
10295c635efSGarrett D'Amore 	{ pre_SS, post_SS, 0 }, /* SS */
10395c635efSGarrett D'Amore 	{ pre_TP, post_TP, 0 }, /* TP */
10495c635efSGarrett D'Amore 	{ pre_PP, NULL, 0 }, /* LP */
10595c635efSGarrett D'Amore 	{ pre_PP, NULL, 0 }, /* PP */
10695c635efSGarrett D'Amore 	{ pre_PP, NULL, 0 }, /* P */
10795c635efSGarrett D'Amore 	{ pre_IP, post_IP, 0 }, /* IP */
10895c635efSGarrett D'Amore 	{ pre_HP, post_HP, 0 }, /* HP */
10995c635efSGarrett D'Amore 	{ NULL, NULL, 0 }, /* SM */
11095c635efSGarrett D'Amore 	{ pre_B, NULL, 0 }, /* SB */
11195c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* BI */
11295c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* IB */
11395c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* BR */
11495c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* RB */
11595c635efSGarrett D'Amore 	{ NULL, NULL, 0 }, /* R */
11695c635efSGarrett D'Amore 	{ pre_B, NULL, 0 }, /* B */
11795c635efSGarrett D'Amore 	{ pre_I, NULL, 0 }, /* I */
11895c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* IR */
11995c635efSGarrett D'Amore 	{ pre_alternate, NULL, 0 }, /* RI */
12095c635efSGarrett D'Amore 	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
12195c635efSGarrett D'Amore 	{ pre_literal, NULL, 0 }, /* nf */
12295c635efSGarrett D'Amore 	{ pre_literal, NULL, 0 }, /* fi */
12395c635efSGarrett D'Amore 	{ NULL, NULL, 0 }, /* RE */
12495c635efSGarrett D'Amore 	{ pre_RS, post_RS, 0 }, /* RS */
12595c635efSGarrett D'Amore 	{ pre_ign, NULL, 0 }, /* DT */
126260e9a87SYuri Pankov 	{ pre_ign, NULL, MAN_NOTEXT }, /* UC */
127698f87a4SGarrett D'Amore 	{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
12895c635efSGarrett D'Amore 	{ pre_ign, NULL, 0 }, /* AT */
12995c635efSGarrett D'Amore 	{ pre_in, NULL, MAN_NOTEXT }, /* in */
13095c635efSGarrett D'Amore 	{ pre_ft, NULL, MAN_NOTEXT }, /* ft */
13195c635efSGarrett D'Amore 	{ pre_OP, NULL, 0 }, /* OP */
132698f87a4SGarrett D'Amore 	{ pre_literal, NULL, 0 }, /* EX */
133698f87a4SGarrett D'Amore 	{ pre_literal, NULL, 0 }, /* EE */
134698f87a4SGarrett D'Amore 	{ pre_UR, post_UR, 0 }, /* UR */
135698f87a4SGarrett D'Amore 	{ NULL, NULL, 0 }, /* UE */
136260e9a87SYuri Pankov 	{ pre_ll, NULL, MAN_NOTEXT }, /* ll */
13795c635efSGarrett D'Amore };
13895c635efSGarrett D'Amore 
13995c635efSGarrett D'Amore 
14095c635efSGarrett D'Amore void
141*371584c2SYuri Pankov terminal_man(void *arg, const struct roff_man *man)
14295c635efSGarrett D'Amore {
14395c635efSGarrett D'Amore 	struct termp		*p;
144*371584c2SYuri Pankov 	struct roff_node	*n;
14595c635efSGarrett D'Amore 	struct mtermp		 mt;
14695c635efSGarrett D'Amore 
14795c635efSGarrett D'Amore 	p = (struct termp *)arg;
14895c635efSGarrett D'Amore 	p->overstep = 0;
149260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin = p->defrmargin;
15095c635efSGarrett D'Amore 	p->tabwidth = term_len(p, 5);
15195c635efSGarrett D'Amore 
15295c635efSGarrett D'Amore 	memset(&mt, 0, sizeof(struct mtermp));
15395c635efSGarrett D'Amore 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
15495c635efSGarrett D'Amore 	mt.offset = term_len(p, p->defindent);
155698f87a4SGarrett D'Amore 	mt.pardist = 1;
15695c635efSGarrett D'Amore 
157*371584c2SYuri Pankov 	n = man->first->child;
158260e9a87SYuri Pankov 	if (p->synopsisonly) {
159260e9a87SYuri Pankov 		while (n != NULL) {
160260e9a87SYuri Pankov 			if (n->tok == MAN_SH &&
161*371584c2SYuri Pankov 			    n->child->child->type == ROFFT_TEXT &&
162260e9a87SYuri Pankov 			    !strcmp(n->child->child->string, "SYNOPSIS")) {
163260e9a87SYuri Pankov 				if (n->child->next->child != NULL)
164260e9a87SYuri Pankov 					print_man_nodelist(p, &mt,
165*371584c2SYuri Pankov 					    n->child->next->child,
166*371584c2SYuri Pankov 					    &man->meta);
167260e9a87SYuri Pankov 				term_newln(p);
168260e9a87SYuri Pankov 				break;
169260e9a87SYuri Pankov 			}
170260e9a87SYuri Pankov 			n = n->next;
171260e9a87SYuri Pankov 		}
172260e9a87SYuri Pankov 	} else {
173260e9a87SYuri Pankov 		if (p->defindent == 0)
174260e9a87SYuri Pankov 			p->defindent = 7;
175*371584c2SYuri Pankov 		term_begin(p, print_man_head, print_man_foot, &man->meta);
176260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE;
177260e9a87SYuri Pankov 		if (n != NULL)
178*371584c2SYuri Pankov 			print_man_nodelist(p, &mt, n, &man->meta);
17995c635efSGarrett D'Amore 		term_end(p);
18095c635efSGarrett D'Amore 	}
18195c635efSGarrett D'Amore }
18295c635efSGarrett D'Amore 
18395c635efSGarrett D'Amore /*
18495c635efSGarrett D'Amore  * Printing leading vertical space before a block.
18595c635efSGarrett D'Amore  * This is used for the paragraph macros.
18695c635efSGarrett D'Amore  * The rules are pretty simple, since there's very little nesting going
18795c635efSGarrett D'Amore  * on here.  Basically, if we're the first within another block (SS/SH),
18895c635efSGarrett D'Amore  * then don't emit vertical space.  If we are (RS), then do.  If not the
18995c635efSGarrett D'Amore  * first, print it.
19095c635efSGarrett D'Amore  */
19195c635efSGarrett D'Amore static void
192*371584c2SYuri Pankov print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
19395c635efSGarrett D'Amore {
194698f87a4SGarrett D'Amore 	int	 i;
19595c635efSGarrett D'Amore 
19695c635efSGarrett D'Amore 	term_newln(p);
19795c635efSGarrett D'Amore 
19895c635efSGarrett D'Amore 	if (n->body && n->body->child)
199*371584c2SYuri Pankov 		if (n->body->child->type == ROFFT_TBL)
20095c635efSGarrett D'Amore 			return;
20195c635efSGarrett D'Amore 
202*371584c2SYuri Pankov 	if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
20395c635efSGarrett D'Amore 		if (NULL == n->prev)
20495c635efSGarrett D'Amore 			return;
20595c635efSGarrett D'Amore 
206698f87a4SGarrett D'Amore 	for (i = 0; i < pardist; i++)
20795c635efSGarrett D'Amore 		term_vspace(p);
20895c635efSGarrett D'Amore }
20995c635efSGarrett D'Amore 
210260e9a87SYuri Pankov 
21195c635efSGarrett D'Amore static int
21295c635efSGarrett D'Amore pre_ign(DECL_ARGS)
21395c635efSGarrett D'Amore {
21495c635efSGarrett D'Amore 
215*371584c2SYuri Pankov 	return 0;
21695c635efSGarrett D'Amore }
21795c635efSGarrett D'Amore 
218260e9a87SYuri Pankov static int
219260e9a87SYuri Pankov pre_ll(DECL_ARGS)
220260e9a87SYuri Pankov {
22195c635efSGarrett D'Amore 
222*371584c2SYuri Pankov 	term_setwidth(p, n->child != NULL ? n->child->string : NULL);
223*371584c2SYuri Pankov 	return 0;
224260e9a87SYuri Pankov }
225260e9a87SYuri Pankov 
22695c635efSGarrett D'Amore static int
22795c635efSGarrett D'Amore pre_I(DECL_ARGS)
22895c635efSGarrett D'Amore {
22995c635efSGarrett D'Amore 
23095c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_UNDER);
231*371584c2SYuri Pankov 	return 1;
23295c635efSGarrett D'Amore }
23395c635efSGarrett D'Amore 
23495c635efSGarrett D'Amore static int
23595c635efSGarrett D'Amore pre_literal(DECL_ARGS)
23695c635efSGarrett D'Amore {
23795c635efSGarrett D'Amore 
23895c635efSGarrett D'Amore 	term_newln(p);
23995c635efSGarrett D'Amore 
240698f87a4SGarrett D'Amore 	if (MAN_nf == n->tok || MAN_EX == n->tok)
24195c635efSGarrett D'Amore 		mt->fl |= MANT_LITERAL;
24295c635efSGarrett D'Amore 	else
24395c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
24495c635efSGarrett D'Amore 
24595c635efSGarrett D'Amore 	/*
24695c635efSGarrett D'Amore 	 * Unlike .IP and .TP, .HP does not have a HEAD.
24795c635efSGarrett D'Amore 	 * So in case a second call to term_flushln() is needed,
24895c635efSGarrett D'Amore 	 * indentation has to be set up explicitly.
24995c635efSGarrett D'Amore 	 */
25095c635efSGarrett D'Amore 	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
25195c635efSGarrett D'Amore 		p->offset = p->rmargin;
25295c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
253698f87a4SGarrett D'Amore 		p->trailspace = 0;
254260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
25595c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
25695c635efSGarrett D'Amore 	}
25795c635efSGarrett D'Amore 
258*371584c2SYuri Pankov 	return 0;
25995c635efSGarrett D'Amore }
26095c635efSGarrett D'Amore 
26195c635efSGarrett D'Amore static int
262698f87a4SGarrett D'Amore pre_PD(DECL_ARGS)
263698f87a4SGarrett D'Amore {
264260e9a87SYuri Pankov 	struct roffsu	 su;
265698f87a4SGarrett D'Amore 
266698f87a4SGarrett D'Amore 	n = n->child;
267260e9a87SYuri Pankov 	if (n == NULL) {
268698f87a4SGarrett D'Amore 		mt->pardist = 1;
269*371584c2SYuri Pankov 		return 0;
270698f87a4SGarrett D'Amore 	}
271*371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
272260e9a87SYuri Pankov 	if (a2roffsu(n->string, &su, SCALE_VS))
273260e9a87SYuri Pankov 		mt->pardist = term_vspan(p, &su);
274*371584c2SYuri Pankov 	return 0;
275698f87a4SGarrett D'Amore }
276698f87a4SGarrett D'Amore 
277698f87a4SGarrett D'Amore static int
27895c635efSGarrett D'Amore pre_alternate(DECL_ARGS)
27995c635efSGarrett D'Amore {
28095c635efSGarrett D'Amore 	enum termfont		 font[2];
281*371584c2SYuri Pankov 	struct roff_node	*nn;
28295c635efSGarrett D'Amore 	int			 savelit, i;
28395c635efSGarrett D'Amore 
28495c635efSGarrett D'Amore 	switch (n->tok) {
285260e9a87SYuri Pankov 	case MAN_RB:
28695c635efSGarrett D'Amore 		font[0] = TERMFONT_NONE;
28795c635efSGarrett D'Amore 		font[1] = TERMFONT_BOLD;
28895c635efSGarrett D'Amore 		break;
289260e9a87SYuri Pankov 	case MAN_RI:
29095c635efSGarrett D'Amore 		font[0] = TERMFONT_NONE;
29195c635efSGarrett D'Amore 		font[1] = TERMFONT_UNDER;
29295c635efSGarrett D'Amore 		break;
293260e9a87SYuri Pankov 	case MAN_BR:
29495c635efSGarrett D'Amore 		font[0] = TERMFONT_BOLD;
29595c635efSGarrett D'Amore 		font[1] = TERMFONT_NONE;
29695c635efSGarrett D'Amore 		break;
297260e9a87SYuri Pankov 	case MAN_BI:
29895c635efSGarrett D'Amore 		font[0] = TERMFONT_BOLD;
29995c635efSGarrett D'Amore 		font[1] = TERMFONT_UNDER;
30095c635efSGarrett D'Amore 		break;
301260e9a87SYuri Pankov 	case MAN_IR:
30295c635efSGarrett D'Amore 		font[0] = TERMFONT_UNDER;
30395c635efSGarrett D'Amore 		font[1] = TERMFONT_NONE;
30495c635efSGarrett D'Amore 		break;
305260e9a87SYuri Pankov 	case MAN_IB:
30695c635efSGarrett D'Amore 		font[0] = TERMFONT_UNDER;
30795c635efSGarrett D'Amore 		font[1] = TERMFONT_BOLD;
30895c635efSGarrett D'Amore 		break;
30995c635efSGarrett D'Amore 	default:
31095c635efSGarrett D'Amore 		abort();
31195c635efSGarrett D'Amore 	}
31295c635efSGarrett D'Amore 
31395c635efSGarrett D'Amore 	savelit = MANT_LITERAL & mt->fl;
31495c635efSGarrett D'Amore 	mt->fl &= ~MANT_LITERAL;
31595c635efSGarrett D'Amore 
31695c635efSGarrett D'Amore 	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
31795c635efSGarrett D'Amore 		term_fontrepl(p, font[i]);
31895c635efSGarrett D'Amore 		if (savelit && NULL == nn->next)
31995c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
320*371584c2SYuri Pankov 		assert(nn->type == ROFFT_TEXT);
321*371584c2SYuri Pankov 		term_word(p, nn->string);
322*371584c2SYuri Pankov 		if (nn->flags & MAN_EOS)
323*371584c2SYuri Pankov                 	p->flags |= TERMP_SENTENCE;
32495c635efSGarrett D'Amore 		if (nn->next)
32595c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
32695c635efSGarrett D'Amore 	}
32795c635efSGarrett D'Amore 
328*371584c2SYuri Pankov 	return 0;
32995c635efSGarrett D'Amore }
33095c635efSGarrett D'Amore 
33195c635efSGarrett D'Amore static int
33295c635efSGarrett D'Amore pre_B(DECL_ARGS)
33395c635efSGarrett D'Amore {
33495c635efSGarrett D'Amore 
33595c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_BOLD);
336*371584c2SYuri Pankov 	return 1;
33795c635efSGarrett D'Amore }
33895c635efSGarrett D'Amore 
33995c635efSGarrett D'Amore static int
34095c635efSGarrett D'Amore pre_OP(DECL_ARGS)
34195c635efSGarrett D'Amore {
34295c635efSGarrett D'Amore 
34395c635efSGarrett D'Amore 	term_word(p, "[");
34495c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
34595c635efSGarrett D'Amore 
34695c635efSGarrett D'Amore 	if (NULL != (n = n->child)) {
34795c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
34895c635efSGarrett D'Amore 		term_word(p, n->string);
34995c635efSGarrett D'Amore 	}
35095c635efSGarrett D'Amore 	if (NULL != n && NULL != n->next) {
35195c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_UNDER);
35295c635efSGarrett D'Amore 		term_word(p, n->next->string);
35395c635efSGarrett D'Amore 	}
35495c635efSGarrett D'Amore 
35595c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_NONE);
35695c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
35795c635efSGarrett D'Amore 	term_word(p, "]");
358*371584c2SYuri Pankov 	return 0;
35995c635efSGarrett D'Amore }
36095c635efSGarrett D'Amore 
36195c635efSGarrett D'Amore static int
36295c635efSGarrett D'Amore pre_ft(DECL_ARGS)
36395c635efSGarrett D'Amore {
36495c635efSGarrett D'Amore 	const char	*cp;
36595c635efSGarrett D'Amore 
36695c635efSGarrett D'Amore 	if (NULL == n->child) {
36795c635efSGarrett D'Amore 		term_fontlast(p);
368*371584c2SYuri Pankov 		return 0;
36995c635efSGarrett D'Amore 	}
37095c635efSGarrett D'Amore 
37195c635efSGarrett D'Amore 	cp = n->child->string;
37295c635efSGarrett D'Amore 	switch (*cp) {
373260e9a87SYuri Pankov 	case '4':
374260e9a87SYuri Pankov 	case '3':
375260e9a87SYuri Pankov 	case 'B':
37695c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
37795c635efSGarrett D'Amore 		break;
378260e9a87SYuri Pankov 	case '2':
379260e9a87SYuri Pankov 	case 'I':
38095c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_UNDER);
38195c635efSGarrett D'Amore 		break;
382260e9a87SYuri Pankov 	case 'P':
38395c635efSGarrett D'Amore 		term_fontlast(p);
38495c635efSGarrett D'Amore 		break;
385260e9a87SYuri Pankov 	case '1':
386260e9a87SYuri Pankov 	case 'C':
387260e9a87SYuri Pankov 	case 'R':
38895c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
38995c635efSGarrett D'Amore 		break;
39095c635efSGarrett D'Amore 	default:
39195c635efSGarrett D'Amore 		break;
39295c635efSGarrett D'Amore 	}
393*371584c2SYuri Pankov 	return 0;
39495c635efSGarrett D'Amore }
39595c635efSGarrett D'Amore 
39695c635efSGarrett D'Amore static int
39795c635efSGarrett D'Amore pre_in(DECL_ARGS)
39895c635efSGarrett D'Amore {
399260e9a87SYuri Pankov 	struct roffsu	 su;
40095c635efSGarrett D'Amore 	const char	*cp;
401260e9a87SYuri Pankov 	size_t		 v;
402260e9a87SYuri Pankov 	int		 less;
40395c635efSGarrett D'Amore 
40495c635efSGarrett D'Amore 	term_newln(p);
40595c635efSGarrett D'Amore 
40695c635efSGarrett D'Amore 	if (NULL == n->child) {
40795c635efSGarrett D'Amore 		p->offset = mt->offset;
408*371584c2SYuri Pankov 		return 0;
40995c635efSGarrett D'Amore 	}
41095c635efSGarrett D'Amore 
41195c635efSGarrett D'Amore 	cp = n->child->string;
41295c635efSGarrett D'Amore 	less = 0;
41395c635efSGarrett D'Amore 
41495c635efSGarrett D'Amore 	if ('-' == *cp)
41595c635efSGarrett D'Amore 		less = -1;
41695c635efSGarrett D'Amore 	else if ('+' == *cp)
41795c635efSGarrett D'Amore 		less = 1;
41895c635efSGarrett D'Amore 	else
41995c635efSGarrett D'Amore 		cp--;
42095c635efSGarrett D'Amore 
421260e9a87SYuri Pankov 	if ( ! a2roffsu(++cp, &su, SCALE_EN))
422*371584c2SYuri Pankov 		return 0;
42395c635efSGarrett D'Amore 
424*371584c2SYuri Pankov 	v = (term_hspan(p, &su) + 11) / 24;
42595c635efSGarrett D'Amore 
42695c635efSGarrett D'Amore 	if (less < 0)
42795c635efSGarrett D'Amore 		p->offset -= p->offset > v ? v : p->offset;
42895c635efSGarrett D'Amore 	else if (less > 0)
42995c635efSGarrett D'Amore 		p->offset += v;
43095c635efSGarrett D'Amore 	else
43195c635efSGarrett D'Amore 		p->offset = v;
432260e9a87SYuri Pankov 	if (p->offset > SHRT_MAX)
433260e9a87SYuri Pankov 		p->offset = term_len(p, p->defindent);
43495c635efSGarrett D'Amore 
435*371584c2SYuri Pankov 	return 0;
43695c635efSGarrett D'Amore }
43795c635efSGarrett D'Amore 
43895c635efSGarrett D'Amore static int
43995c635efSGarrett D'Amore pre_sp(DECL_ARGS)
44095c635efSGarrett D'Amore {
441260e9a87SYuri Pankov 	struct roffsu	 su;
442260e9a87SYuri Pankov 	int		 i, len;
44395c635efSGarrett D'Amore 
44495c635efSGarrett D'Amore 	if ((NULL == n->prev && n->parent)) {
445698f87a4SGarrett D'Amore 		switch (n->parent->tok) {
446260e9a87SYuri Pankov 		case MAN_SH:
447260e9a87SYuri Pankov 		case MAN_SS:
448260e9a87SYuri Pankov 		case MAN_PP:
449260e9a87SYuri Pankov 		case MAN_LP:
450260e9a87SYuri Pankov 		case MAN_P:
451*371584c2SYuri Pankov 			return 0;
452698f87a4SGarrett D'Amore 		default:
453698f87a4SGarrett D'Amore 			break;
454698f87a4SGarrett D'Amore 		}
45595c635efSGarrett D'Amore 	}
45695c635efSGarrett D'Amore 
457260e9a87SYuri Pankov 	if (n->tok == MAN_br)
45895c635efSGarrett D'Amore 		len = 0;
459260e9a87SYuri Pankov 	else if (n->child == NULL)
460698f87a4SGarrett D'Amore 		len = 1;
461260e9a87SYuri Pankov 	else {
462260e9a87SYuri Pankov 		if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
463260e9a87SYuri Pankov 			su.scale = 1.0;
464260e9a87SYuri Pankov 		len = term_vspan(p, &su);
46595c635efSGarrett D'Amore 	}
46695c635efSGarrett D'Amore 
467260e9a87SYuri Pankov 	if (len == 0)
46895c635efSGarrett D'Amore 		term_newln(p);
469260e9a87SYuri Pankov 	else if (len < 0)
470260e9a87SYuri Pankov 		p->skipvsp -= len;
471698f87a4SGarrett D'Amore 	else
47295c635efSGarrett D'Amore 		for (i = 0; i < len; i++)
47395c635efSGarrett D'Amore 			term_vspace(p);
47495c635efSGarrett D'Amore 
475*371584c2SYuri Pankov 	/*
476*371584c2SYuri Pankov 	 * Handle an explicit break request in the same way
477*371584c2SYuri Pankov 	 * as an overflowing line.
478*371584c2SYuri Pankov 	 */
479*371584c2SYuri Pankov 
480*371584c2SYuri Pankov 	if (p->flags & TERMP_BRIND) {
481*371584c2SYuri Pankov 		p->offset = p->rmargin;
482*371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
483*371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
484*371584c2SYuri Pankov 	}
485*371584c2SYuri Pankov 
486*371584c2SYuri Pankov 	return 0;
48795c635efSGarrett D'Amore }
48895c635efSGarrett D'Amore 
48995c635efSGarrett D'Amore static int
49095c635efSGarrett D'Amore pre_HP(DECL_ARGS)
49195c635efSGarrett D'Amore {
492260e9a87SYuri Pankov 	struct roffsu		 su;
493*371584c2SYuri Pankov 	const struct roff_node	*nn;
494260e9a87SYuri Pankov 	int			 len;
49595c635efSGarrett D'Amore 
49695c635efSGarrett D'Amore 	switch (n->type) {
497*371584c2SYuri Pankov 	case ROFFT_BLOCK:
498698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
499*371584c2SYuri Pankov 		return 1;
500*371584c2SYuri Pankov 	case ROFFT_BODY:
50195c635efSGarrett D'Amore 		break;
50295c635efSGarrett D'Amore 	default:
503*371584c2SYuri Pankov 		return 0;
50495c635efSGarrett D'Amore 	}
50595c635efSGarrett D'Amore 
506698f87a4SGarrett D'Amore 	if ( ! (MANT_LITERAL & mt->fl)) {
507260e9a87SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
508698f87a4SGarrett D'Amore 		p->trailspace = 2;
509698f87a4SGarrett D'Amore 	}
510698f87a4SGarrett D'Amore 
51195c635efSGarrett D'Amore 	/* Calculate offset. */
51295c635efSGarrett D'Amore 
513260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
514260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
515*371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
516260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
517260e9a87SYuri Pankov 			len = -mt->offset;
518260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
519260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
520260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
521260e9a87SYuri Pankov 	} else
522260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
52395c635efSGarrett D'Amore 
52495c635efSGarrett D'Amore 	p->offset = mt->offset;
52595c635efSGarrett D'Amore 	p->rmargin = mt->offset + len;
526*371584c2SYuri Pankov 	return 1;
52795c635efSGarrett D'Amore }
52895c635efSGarrett D'Amore 
52995c635efSGarrett D'Amore static void
53095c635efSGarrett D'Amore post_HP(DECL_ARGS)
53195c635efSGarrett D'Amore {
53295c635efSGarrett D'Amore 
53395c635efSGarrett D'Amore 	switch (n->type) {
534*371584c2SYuri Pankov 	case ROFFT_BODY:
535698f87a4SGarrett D'Amore 		term_newln(p);
536*371584c2SYuri Pankov 
537*371584c2SYuri Pankov 		/*
538*371584c2SYuri Pankov 		 * Compatibility with a groff bug.
539*371584c2SYuri Pankov 		 * The .HP macro uses the undocumented .tag request
540*371584c2SYuri Pankov 		 * which causes a line break and cancels no-space
541*371584c2SYuri Pankov 		 * mode even if there isn't any output.
542*371584c2SYuri Pankov 		 */
543*371584c2SYuri Pankov 
544*371584c2SYuri Pankov 		if (n->child == NULL)
545*371584c2SYuri Pankov 			term_vspace(p);
546*371584c2SYuri Pankov 
547260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
548698f87a4SGarrett D'Amore 		p->trailspace = 0;
54995c635efSGarrett D'Amore 		p->offset = mt->offset;
55095c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
55195c635efSGarrett D'Amore 		break;
55295c635efSGarrett D'Amore 	default:
55395c635efSGarrett D'Amore 		break;
55495c635efSGarrett D'Amore 	}
55595c635efSGarrett D'Amore }
55695c635efSGarrett D'Amore 
55795c635efSGarrett D'Amore static int
55895c635efSGarrett D'Amore pre_PP(DECL_ARGS)
55995c635efSGarrett D'Amore {
56095c635efSGarrett D'Amore 
56195c635efSGarrett D'Amore 	switch (n->type) {
562*371584c2SYuri Pankov 	case ROFFT_BLOCK:
56395c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
564698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
56595c635efSGarrett D'Amore 		break;
56695c635efSGarrett D'Amore 	default:
56795c635efSGarrett D'Amore 		p->offset = mt->offset;
56895c635efSGarrett D'Amore 		break;
56995c635efSGarrett D'Amore 	}
57095c635efSGarrett D'Amore 
571*371584c2SYuri Pankov 	return n->type != ROFFT_HEAD;
57295c635efSGarrett D'Amore }
57395c635efSGarrett D'Amore 
57495c635efSGarrett D'Amore static int
57595c635efSGarrett D'Amore pre_IP(DECL_ARGS)
57695c635efSGarrett D'Amore {
577260e9a87SYuri Pankov 	struct roffsu		 su;
578*371584c2SYuri Pankov 	const struct roff_node	*nn;
579260e9a87SYuri Pankov 	int			 len, savelit;
58095c635efSGarrett D'Amore 
58195c635efSGarrett D'Amore 	switch (n->type) {
582*371584c2SYuri Pankov 	case ROFFT_BODY:
58395c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
58495c635efSGarrett D'Amore 		break;
585*371584c2SYuri Pankov 	case ROFFT_HEAD:
58695c635efSGarrett D'Amore 		p->flags |= TERMP_NOBREAK;
587698f87a4SGarrett D'Amore 		p->trailspace = 1;
58895c635efSGarrett D'Amore 		break;
589*371584c2SYuri Pankov 	case ROFFT_BLOCK:
590698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
59195c635efSGarrett D'Amore 		/* FALLTHROUGH */
59295c635efSGarrett D'Amore 	default:
593*371584c2SYuri Pankov 		return 1;
59495c635efSGarrett D'Amore 	}
59595c635efSGarrett D'Amore 
59695c635efSGarrett D'Amore 	/* Calculate the offset from the optional second argument. */
597260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
598260e9a87SYuri Pankov 	    (nn = nn->next) != NULL &&
599260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
600*371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
601260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
602260e9a87SYuri Pankov 			len = -mt->offset;
603260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
604260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
605260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
606260e9a87SYuri Pankov 	} else
607260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
60895c635efSGarrett D'Amore 
60995c635efSGarrett D'Amore 	switch (n->type) {
610*371584c2SYuri Pankov 	case ROFFT_HEAD:
61195c635efSGarrett D'Amore 		p->offset = mt->offset;
61295c635efSGarrett D'Amore 		p->rmargin = mt->offset + len;
61395c635efSGarrett D'Amore 
61495c635efSGarrett D'Amore 		savelit = MANT_LITERAL & mt->fl;
61595c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
61695c635efSGarrett D'Amore 
61795c635efSGarrett D'Amore 		if (n->child)
618698f87a4SGarrett D'Amore 			print_man_node(p, mt, n->child, meta);
61995c635efSGarrett D'Amore 
62095c635efSGarrett D'Amore 		if (savelit)
62195c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
62295c635efSGarrett D'Amore 
623*371584c2SYuri Pankov 		return 0;
624*371584c2SYuri Pankov 	case ROFFT_BODY:
62595c635efSGarrett D'Amore 		p->offset = mt->offset + len;
62695c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
62795c635efSGarrett D'Amore 		break;
62895c635efSGarrett D'Amore 	default:
62995c635efSGarrett D'Amore 		break;
63095c635efSGarrett D'Amore 	}
63195c635efSGarrett D'Amore 
632*371584c2SYuri Pankov 	return 1;
63395c635efSGarrett D'Amore }
63495c635efSGarrett D'Amore 
63595c635efSGarrett D'Amore static void
63695c635efSGarrett D'Amore post_IP(DECL_ARGS)
63795c635efSGarrett D'Amore {
63895c635efSGarrett D'Amore 
63995c635efSGarrett D'Amore 	switch (n->type) {
640*371584c2SYuri Pankov 	case ROFFT_HEAD:
64195c635efSGarrett D'Amore 		term_flushln(p);
64295c635efSGarrett D'Amore 		p->flags &= ~TERMP_NOBREAK;
643698f87a4SGarrett D'Amore 		p->trailspace = 0;
64495c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
64595c635efSGarrett D'Amore 		break;
646*371584c2SYuri Pankov 	case ROFFT_BODY:
64795c635efSGarrett D'Amore 		term_newln(p);
648698f87a4SGarrett D'Amore 		p->offset = mt->offset;
64995c635efSGarrett D'Amore 		break;
65095c635efSGarrett D'Amore 	default:
65195c635efSGarrett D'Amore 		break;
65295c635efSGarrett D'Amore 	}
65395c635efSGarrett D'Amore }
65495c635efSGarrett D'Amore 
65595c635efSGarrett D'Amore static int
65695c635efSGarrett D'Amore pre_TP(DECL_ARGS)
65795c635efSGarrett D'Amore {
658260e9a87SYuri Pankov 	struct roffsu		 su;
659*371584c2SYuri Pankov 	struct roff_node	*nn;
660260e9a87SYuri Pankov 	int			 len, savelit;
66195c635efSGarrett D'Amore 
66295c635efSGarrett D'Amore 	switch (n->type) {
663*371584c2SYuri Pankov 	case ROFFT_HEAD:
664*371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
665698f87a4SGarrett D'Amore 		p->trailspace = 1;
66695c635efSGarrett D'Amore 		break;
667*371584c2SYuri Pankov 	case ROFFT_BODY:
66895c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
66995c635efSGarrett D'Amore 		break;
670*371584c2SYuri Pankov 	case ROFFT_BLOCK:
671698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
67295c635efSGarrett D'Amore 		/* FALLTHROUGH */
67395c635efSGarrett D'Amore 	default:
674*371584c2SYuri Pankov 		return 1;
67595c635efSGarrett D'Amore 	}
67695c635efSGarrett D'Amore 
67795c635efSGarrett D'Amore 	/* Calculate offset. */
67895c635efSGarrett D'Amore 
679260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
680260e9a87SYuri Pankov 	    nn->string != NULL && ! (MAN_LINE & nn->flags) &&
681260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
682*371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
683260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
684260e9a87SYuri Pankov 			len = -mt->offset;
685260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
686260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
687260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
688260e9a87SYuri Pankov 	} else
689260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
69095c635efSGarrett D'Amore 
69195c635efSGarrett D'Amore 	switch (n->type) {
692*371584c2SYuri Pankov 	case ROFFT_HEAD:
69395c635efSGarrett D'Amore 		p->offset = mt->offset;
69495c635efSGarrett D'Amore 		p->rmargin = mt->offset + len;
69595c635efSGarrett D'Amore 
69695c635efSGarrett D'Amore 		savelit = MANT_LITERAL & mt->fl;
69795c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
69895c635efSGarrett D'Amore 
69995c635efSGarrett D'Amore 		/* Don't print same-line elements. */
700260e9a87SYuri Pankov 		nn = n->child;
701260e9a87SYuri Pankov 		while (NULL != nn && 0 == (MAN_LINE & nn->flags))
702260e9a87SYuri Pankov 			nn = nn->next;
703260e9a87SYuri Pankov 
704260e9a87SYuri Pankov 		while (NULL != nn) {
705698f87a4SGarrett D'Amore 			print_man_node(p, mt, nn, meta);
706260e9a87SYuri Pankov 			nn = nn->next;
707260e9a87SYuri Pankov 		}
70895c635efSGarrett D'Amore 
70995c635efSGarrett D'Amore 		if (savelit)
71095c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
711*371584c2SYuri Pankov 		return 0;
712*371584c2SYuri Pankov 	case ROFFT_BODY:
71395c635efSGarrett D'Amore 		p->offset = mt->offset + len;
71495c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
715698f87a4SGarrett D'Amore 		p->trailspace = 0;
716*371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
71795c635efSGarrett D'Amore 		break;
71895c635efSGarrett D'Amore 	default:
71995c635efSGarrett D'Amore 		break;
72095c635efSGarrett D'Amore 	}
72195c635efSGarrett D'Amore 
722*371584c2SYuri Pankov 	return 1;
72395c635efSGarrett D'Amore }
72495c635efSGarrett D'Amore 
72595c635efSGarrett D'Amore static void
72695c635efSGarrett D'Amore post_TP(DECL_ARGS)
72795c635efSGarrett D'Amore {
72895c635efSGarrett D'Amore 
72995c635efSGarrett D'Amore 	switch (n->type) {
730*371584c2SYuri Pankov 	case ROFFT_HEAD:
73195c635efSGarrett D'Amore 		term_flushln(p);
73295c635efSGarrett D'Amore 		break;
733*371584c2SYuri Pankov 	case ROFFT_BODY:
73495c635efSGarrett D'Amore 		term_newln(p);
735698f87a4SGarrett D'Amore 		p->offset = mt->offset;
73695c635efSGarrett D'Amore 		break;
73795c635efSGarrett D'Amore 	default:
73895c635efSGarrett D'Amore 		break;
73995c635efSGarrett D'Amore 	}
74095c635efSGarrett D'Amore }
74195c635efSGarrett D'Amore 
74295c635efSGarrett D'Amore static int
74395c635efSGarrett D'Amore pre_SS(DECL_ARGS)
74495c635efSGarrett D'Amore {
745698f87a4SGarrett D'Amore 	int	 i;
74695c635efSGarrett D'Amore 
74795c635efSGarrett D'Amore 	switch (n->type) {
748*371584c2SYuri Pankov 	case ROFFT_BLOCK:
74995c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
75095c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
75195c635efSGarrett D'Amore 		mt->offset = term_len(p, p->defindent);
752260e9a87SYuri Pankov 
753260e9a87SYuri Pankov 		/*
754260e9a87SYuri Pankov 		 * No vertical space before the first subsection
755260e9a87SYuri Pankov 		 * and after an empty subsection.
756260e9a87SYuri Pankov 		 */
757260e9a87SYuri Pankov 
758260e9a87SYuri Pankov 		do {
759260e9a87SYuri Pankov 			n = n->prev;
760*371584c2SYuri Pankov 		} while (n != NULL && n->tok != TOKEN_NONE &&
761*371584c2SYuri Pankov 		    termacts[n->tok].flags & MAN_NOTEXT);
762260e9a87SYuri Pankov 		if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
76395c635efSGarrett D'Amore 			break;
764260e9a87SYuri Pankov 
765698f87a4SGarrett D'Amore 		for (i = 0; i < mt->pardist; i++)
76695c635efSGarrett D'Amore 			term_vspace(p);
76795c635efSGarrett D'Amore 		break;
768*371584c2SYuri Pankov 	case ROFFT_HEAD:
76995c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
770698f87a4SGarrett D'Amore 		p->offset = term_len(p, 3);
771*371584c2SYuri Pankov 		p->rmargin = mt->offset;
772*371584c2SYuri Pankov 		p->trailspace = mt->offset;
773*371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
77495c635efSGarrett D'Amore 		break;
775*371584c2SYuri Pankov 	case ROFFT_BODY:
77695c635efSGarrett D'Amore 		p->offset = mt->offset;
777*371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
778*371584c2SYuri Pankov 		p->trailspace = 0;
779*371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
78095c635efSGarrett D'Amore 		break;
78195c635efSGarrett D'Amore 	default:
78295c635efSGarrett D'Amore 		break;
78395c635efSGarrett D'Amore 	}
78495c635efSGarrett D'Amore 
785*371584c2SYuri Pankov 	return 1;
78695c635efSGarrett D'Amore }
78795c635efSGarrett D'Amore 
78895c635efSGarrett D'Amore static void
78995c635efSGarrett D'Amore post_SS(DECL_ARGS)
79095c635efSGarrett D'Amore {
79195c635efSGarrett D'Amore 
79295c635efSGarrett D'Amore 	switch (n->type) {
793*371584c2SYuri Pankov 	case ROFFT_HEAD:
79495c635efSGarrett D'Amore 		term_newln(p);
79595c635efSGarrett D'Amore 		break;
796*371584c2SYuri Pankov 	case ROFFT_BODY:
79795c635efSGarrett D'Amore 		term_newln(p);
79895c635efSGarrett D'Amore 		break;
79995c635efSGarrett D'Amore 	default:
80095c635efSGarrett D'Amore 		break;
80195c635efSGarrett D'Amore 	}
80295c635efSGarrett D'Amore }
80395c635efSGarrett D'Amore 
80495c635efSGarrett D'Amore static int
80595c635efSGarrett D'Amore pre_SH(DECL_ARGS)
80695c635efSGarrett D'Amore {
807698f87a4SGarrett D'Amore 	int	 i;
80895c635efSGarrett D'Amore 
80995c635efSGarrett D'Amore 	switch (n->type) {
810*371584c2SYuri Pankov 	case ROFFT_BLOCK:
81195c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
81295c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
81395c635efSGarrett D'Amore 		mt->offset = term_len(p, p->defindent);
814260e9a87SYuri Pankov 
815260e9a87SYuri Pankov 		/*
816260e9a87SYuri Pankov 		 * No vertical space before the first section
817260e9a87SYuri Pankov 		 * and after an empty section.
818260e9a87SYuri Pankov 		 */
819260e9a87SYuri Pankov 
820260e9a87SYuri Pankov 		do {
821260e9a87SYuri Pankov 			n = n->prev;
822260e9a87SYuri Pankov 		} while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT);
823260e9a87SYuri Pankov 		if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
82495c635efSGarrett D'Amore 			break;
825260e9a87SYuri Pankov 
826698f87a4SGarrett D'Amore 		for (i = 0; i < mt->pardist; i++)
82795c635efSGarrett D'Amore 			term_vspace(p);
82895c635efSGarrett D'Amore 		break;
829*371584c2SYuri Pankov 	case ROFFT_HEAD:
83095c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
83195c635efSGarrett D'Amore 		p->offset = 0;
832*371584c2SYuri Pankov 		p->rmargin = mt->offset;
833*371584c2SYuri Pankov 		p->trailspace = mt->offset;
834*371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
83595c635efSGarrett D'Amore 		break;
836*371584c2SYuri Pankov 	case ROFFT_BODY:
83795c635efSGarrett D'Amore 		p->offset = mt->offset;
838*371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
839*371584c2SYuri Pankov 		p->trailspace = 0;
840*371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
84195c635efSGarrett D'Amore 		break;
84295c635efSGarrett D'Amore 	default:
84395c635efSGarrett D'Amore 		break;
84495c635efSGarrett D'Amore 	}
84595c635efSGarrett D'Amore 
846*371584c2SYuri Pankov 	return 1;
84795c635efSGarrett D'Amore }
84895c635efSGarrett D'Amore 
84995c635efSGarrett D'Amore static void
85095c635efSGarrett D'Amore post_SH(DECL_ARGS)
85195c635efSGarrett D'Amore {
85295c635efSGarrett D'Amore 
85395c635efSGarrett D'Amore 	switch (n->type) {
854*371584c2SYuri Pankov 	case ROFFT_HEAD:
85595c635efSGarrett D'Amore 		term_newln(p);
85695c635efSGarrett D'Amore 		break;
857*371584c2SYuri Pankov 	case ROFFT_BODY:
85895c635efSGarrett D'Amore 		term_newln(p);
85995c635efSGarrett D'Amore 		break;
86095c635efSGarrett D'Amore 	default:
86195c635efSGarrett D'Amore 		break;
86295c635efSGarrett D'Amore 	}
86395c635efSGarrett D'Amore }
86495c635efSGarrett D'Amore 
86595c635efSGarrett D'Amore static int
86695c635efSGarrett D'Amore pre_RS(DECL_ARGS)
86795c635efSGarrett D'Amore {
868260e9a87SYuri Pankov 	struct roffsu	 su;
86995c635efSGarrett D'Amore 
87095c635efSGarrett D'Amore 	switch (n->type) {
871*371584c2SYuri Pankov 	case ROFFT_BLOCK:
87295c635efSGarrett D'Amore 		term_newln(p);
873*371584c2SYuri Pankov 		return 1;
874*371584c2SYuri Pankov 	case ROFFT_HEAD:
875*371584c2SYuri Pankov 		return 0;
87695c635efSGarrett D'Amore 	default:
87795c635efSGarrett D'Amore 		break;
87895c635efSGarrett D'Amore 	}
87995c635efSGarrett D'Amore 
880260e9a87SYuri Pankov 	n = n->parent->head;
881260e9a87SYuri Pankov 	n->aux = SHRT_MAX + 1;
882*371584c2SYuri Pankov 	if (n->child == NULL)
883*371584c2SYuri Pankov 		n->aux = mt->lmargin[mt->lmargincur];
884*371584c2SYuri Pankov 	else if (a2roffsu(n->child->string, &su, SCALE_EN))
885*371584c2SYuri Pankov 		n->aux = term_hspan(p, &su) / 24;
886260e9a87SYuri Pankov 	if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
887260e9a87SYuri Pankov 		n->aux = -mt->offset;
888260e9a87SYuri Pankov 	else if (n->aux > SHRT_MAX)
889260e9a87SYuri Pankov 		n->aux = term_len(p, p->defindent);
89095c635efSGarrett D'Amore 
891260e9a87SYuri Pankov 	mt->offset += n->aux;
892260e9a87SYuri Pankov 	p->offset = mt->offset;
89395c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
89495c635efSGarrett D'Amore 
89595c635efSGarrett D'Amore 	if (++mt->lmarginsz < MAXMARGINS)
89695c635efSGarrett D'Amore 		mt->lmargincur = mt->lmarginsz;
89795c635efSGarrett D'Amore 
898*371584c2SYuri Pankov 	mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
899*371584c2SYuri Pankov 	return 1;
90095c635efSGarrett D'Amore }
90195c635efSGarrett D'Amore 
90295c635efSGarrett D'Amore static void
90395c635efSGarrett D'Amore post_RS(DECL_ARGS)
90495c635efSGarrett D'Amore {
90595c635efSGarrett D'Amore 
90695c635efSGarrett D'Amore 	switch (n->type) {
907*371584c2SYuri Pankov 	case ROFFT_BLOCK:
90895c635efSGarrett D'Amore 		return;
909*371584c2SYuri Pankov 	case ROFFT_HEAD:
91095c635efSGarrett D'Amore 		return;
91195c635efSGarrett D'Amore 	default:
91295c635efSGarrett D'Amore 		term_newln(p);
91395c635efSGarrett D'Amore 		break;
91495c635efSGarrett D'Amore 	}
91595c635efSGarrett D'Amore 
916260e9a87SYuri Pankov 	mt->offset -= n->parent->head->aux;
91795c635efSGarrett D'Amore 	p->offset = mt->offset;
91895c635efSGarrett D'Amore 
91995c635efSGarrett D'Amore 	if (--mt->lmarginsz < MAXMARGINS)
92095c635efSGarrett D'Amore 		mt->lmargincur = mt->lmarginsz;
92195c635efSGarrett D'Amore }
92295c635efSGarrett D'Amore 
923698f87a4SGarrett D'Amore static int
924698f87a4SGarrett D'Amore pre_UR(DECL_ARGS)
925698f87a4SGarrett D'Amore {
926698f87a4SGarrett D'Amore 
927*371584c2SYuri Pankov 	return n->type != ROFFT_HEAD;
928698f87a4SGarrett D'Amore }
929698f87a4SGarrett D'Amore 
930698f87a4SGarrett D'Amore static void
931698f87a4SGarrett D'Amore post_UR(DECL_ARGS)
932698f87a4SGarrett D'Amore {
933698f87a4SGarrett D'Amore 
934*371584c2SYuri Pankov 	if (n->type != ROFFT_BLOCK)
935698f87a4SGarrett D'Amore 		return;
936698f87a4SGarrett D'Amore 
937698f87a4SGarrett D'Amore 	term_word(p, "<");
938698f87a4SGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
939698f87a4SGarrett D'Amore 
940698f87a4SGarrett D'Amore 	if (NULL != n->child->child)
941698f87a4SGarrett D'Amore 		print_man_node(p, mt, n->child->child, meta);
942698f87a4SGarrett D'Amore 
943698f87a4SGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
944698f87a4SGarrett D'Amore 	term_word(p, ">");
945698f87a4SGarrett D'Amore }
946698f87a4SGarrett D'Amore 
94795c635efSGarrett D'Amore static void
94895c635efSGarrett D'Amore print_man_node(DECL_ARGS)
94995c635efSGarrett D'Amore {
95095c635efSGarrett D'Amore 	size_t		 rm, rmax;
95195c635efSGarrett D'Amore 	int		 c;
95295c635efSGarrett D'Amore 
95395c635efSGarrett D'Amore 	switch (n->type) {
954*371584c2SYuri Pankov 	case ROFFT_TEXT:
95595c635efSGarrett D'Amore 		/*
95695c635efSGarrett D'Amore 		 * If we have a blank line, output a vertical space.
95795c635efSGarrett D'Amore 		 * If we have a space as the first character, break
95895c635efSGarrett D'Amore 		 * before printing the line's data.
95995c635efSGarrett D'Amore 		 */
96095c635efSGarrett D'Amore 		if ('\0' == *n->string) {
96195c635efSGarrett D'Amore 			term_vspace(p);
96295c635efSGarrett D'Amore 			return;
96395c635efSGarrett D'Amore 		} else if (' ' == *n->string && MAN_LINE & n->flags)
96495c635efSGarrett D'Amore 			term_newln(p);
96595c635efSGarrett D'Amore 
96695c635efSGarrett D'Amore 		term_word(p, n->string);
967698f87a4SGarrett D'Amore 		goto out;
96895c635efSGarrett D'Amore 
969*371584c2SYuri Pankov 	case ROFFT_EQN:
970260e9a87SYuri Pankov 		if ( ! (n->flags & MAN_LINE))
971260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
97295c635efSGarrett D'Amore 		term_eqn(p, n->eqn);
973260e9a87SYuri Pankov 		if (n->next != NULL && ! (n->next->flags & MAN_LINE))
974260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
97595c635efSGarrett D'Amore 		return;
976*371584c2SYuri Pankov 	case ROFFT_TBL:
977260e9a87SYuri Pankov 		if (p->tbl.cols == NULL)
978260e9a87SYuri Pankov 			term_vspace(p);
97995c635efSGarrett D'Amore 		term_tbl(p, n->span);
98095c635efSGarrett D'Amore 		return;
98195c635efSGarrett D'Amore 	default:
98295c635efSGarrett D'Amore 		break;
98395c635efSGarrett D'Amore 	}
98495c635efSGarrett D'Amore 
98595c635efSGarrett D'Amore 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
98695c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
98795c635efSGarrett D'Amore 
98895c635efSGarrett D'Amore 	c = 1;
98995c635efSGarrett D'Amore 	if (termacts[n->tok].pre)
990698f87a4SGarrett D'Amore 		c = (*termacts[n->tok].pre)(p, mt, n, meta);
99195c635efSGarrett D'Amore 
99295c635efSGarrett D'Amore 	if (c && n->child)
993698f87a4SGarrett D'Amore 		print_man_nodelist(p, mt, n->child, meta);
99495c635efSGarrett D'Amore 
99595c635efSGarrett D'Amore 	if (termacts[n->tok].post)
996698f87a4SGarrett D'Amore 		(*termacts[n->tok].post)(p, mt, n, meta);
99795c635efSGarrett D'Amore 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
99895c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
99995c635efSGarrett D'Amore 
1000698f87a4SGarrett D'Amore out:
1001698f87a4SGarrett D'Amore 	/*
1002698f87a4SGarrett D'Amore 	 * If we're in a literal context, make sure that words
1003698f87a4SGarrett D'Amore 	 * together on the same line stay together.  This is a
1004698f87a4SGarrett D'Amore 	 * POST-printing call, so we check the NEXT word.  Since
1005698f87a4SGarrett D'Amore 	 * -man doesn't have nested macros, we don't need to be
1006698f87a4SGarrett D'Amore 	 * more specific than this.
1007698f87a4SGarrett D'Amore 	 */
1008260e9a87SYuri Pankov 	if (mt->fl & MANT_LITERAL &&
1009260e9a87SYuri Pankov 	    ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1010260e9a87SYuri Pankov 	    (n->next == NULL || n->next->flags & MAN_LINE)) {
1011698f87a4SGarrett D'Amore 		rm = p->rmargin;
1012698f87a4SGarrett D'Amore 		rmax = p->maxrmargin;
1013698f87a4SGarrett D'Amore 		p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1014698f87a4SGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
1015260e9a87SYuri Pankov 		if (n->string != NULL && *n->string != '\0')
1016698f87a4SGarrett D'Amore 			term_flushln(p);
1017698f87a4SGarrett D'Amore 		else
1018698f87a4SGarrett D'Amore 			term_newln(p);
1019698f87a4SGarrett D'Amore 		if (rm < rmax && n->parent->tok == MAN_HP) {
1020698f87a4SGarrett D'Amore 			p->offset = rm;
1021698f87a4SGarrett D'Amore 			p->rmargin = rmax;
1022698f87a4SGarrett D'Amore 		} else
1023698f87a4SGarrett D'Amore 			p->rmargin = rm;
1024698f87a4SGarrett D'Amore 		p->maxrmargin = rmax;
1025698f87a4SGarrett D'Amore 	}
102695c635efSGarrett D'Amore 	if (MAN_EOS & n->flags)
102795c635efSGarrett D'Amore 		p->flags |= TERMP_SENTENCE;
102895c635efSGarrett D'Amore }
102995c635efSGarrett D'Amore 
103095c635efSGarrett D'Amore 
103195c635efSGarrett D'Amore static void
103295c635efSGarrett D'Amore print_man_nodelist(DECL_ARGS)
103395c635efSGarrett D'Amore {
103495c635efSGarrett D'Amore 
1035260e9a87SYuri Pankov 	while (n != NULL) {
1036698f87a4SGarrett D'Amore 		print_man_node(p, mt, n, meta);
1037260e9a87SYuri Pankov 		n = n->next;
103895c635efSGarrett D'Amore 	}
1039260e9a87SYuri Pankov }
104095c635efSGarrett D'Amore 
104195c635efSGarrett D'Amore static void
1042*371584c2SYuri Pankov print_man_foot(struct termp *p, const struct roff_meta *meta)
104395c635efSGarrett D'Amore {
1044260e9a87SYuri Pankov 	char			*title;
1045260e9a87SYuri Pankov 	size_t			 datelen, titlen;
104695c635efSGarrett D'Amore 
104795c635efSGarrett D'Amore 	assert(meta->title);
104895c635efSGarrett D'Amore 	assert(meta->msec);
104995c635efSGarrett D'Amore 	assert(meta->date);
105095c635efSGarrett D'Amore 
105195c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_NONE);
105295c635efSGarrett D'Amore 
1053260e9a87SYuri Pankov 	if (meta->hasbody)
105495c635efSGarrett D'Amore 		term_vspace(p);
105595c635efSGarrett D'Amore 
105695c635efSGarrett D'Amore 	/*
105795c635efSGarrett D'Amore 	 * Temporary, undocumented option to imitate mdoc(7) output.
1058*371584c2SYuri Pankov 	 * In the bottom right corner, use the operating system
1059*371584c2SYuri Pankov 	 * instead of the title.
106095c635efSGarrett D'Amore 	 */
106195c635efSGarrett D'Amore 
106295c635efSGarrett D'Amore 	if ( ! p->mdocstyle) {
1063260e9a87SYuri Pankov 		if (meta->hasbody) {
106495c635efSGarrett D'Amore 			term_vspace(p);
106595c635efSGarrett D'Amore 			term_vspace(p);
1066260e9a87SYuri Pankov 		}
1067260e9a87SYuri Pankov 		mandoc_asprintf(&title, "%s(%s)",
1068260e9a87SYuri Pankov 		    meta->title, meta->msec);
1069*371584c2SYuri Pankov 	} else if (meta->os) {
1070*371584c2SYuri Pankov 		title = mandoc_strdup(meta->os);
107195c635efSGarrett D'Amore 	} else {
1072260e9a87SYuri Pankov 		title = mandoc_strdup("");
107395c635efSGarrett D'Amore 	}
107495c635efSGarrett D'Amore 	datelen = term_strlen(p, meta->date);
107595c635efSGarrett D'Amore 
1076*371584c2SYuri Pankov 	/* Bottom left corner: operating system. */
107795c635efSGarrett D'Amore 
107895c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1079698f87a4SGarrett D'Amore 	p->trailspace = 1;
108095c635efSGarrett D'Amore 	p->offset = 0;
1081260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin > datelen ?
1082260e9a87SYuri Pankov 	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
108395c635efSGarrett D'Amore 
1084*371584c2SYuri Pankov 	if (meta->os)
1085*371584c2SYuri Pankov 		term_word(p, meta->os);
108695c635efSGarrett D'Amore 	term_flushln(p);
108795c635efSGarrett D'Amore 
108895c635efSGarrett D'Amore 	/* At the bottom in the middle: manual date. */
108995c635efSGarrett D'Amore 
109095c635efSGarrett D'Amore 	p->offset = p->rmargin;
1091260e9a87SYuri Pankov 	titlen = term_strlen(p, title);
1092260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
1093260e9a87SYuri Pankov 	p->flags |= TERMP_NOSPACE;
109495c635efSGarrett D'Amore 
109595c635efSGarrett D'Amore 	term_word(p, meta->date);
109695c635efSGarrett D'Amore 	term_flushln(p);
109795c635efSGarrett D'Amore 
109895c635efSGarrett D'Amore 	/* Bottom right corner: manual title and section. */
109995c635efSGarrett D'Amore 
110095c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
110195c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1102698f87a4SGarrett D'Amore 	p->trailspace = 0;
110395c635efSGarrett D'Amore 	p->offset = p->rmargin;
110495c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
110595c635efSGarrett D'Amore 
110695c635efSGarrett D'Amore 	term_word(p, title);
110795c635efSGarrett D'Amore 	term_flushln(p);
1108260e9a87SYuri Pankov 	free(title);
110995c635efSGarrett D'Amore }
111095c635efSGarrett D'Amore 
111195c635efSGarrett D'Amore static void
1112*371584c2SYuri Pankov print_man_head(struct termp *p, const struct roff_meta *meta)
111395c635efSGarrett D'Amore {
1114260e9a87SYuri Pankov 	const char		*volume;
1115260e9a87SYuri Pankov 	char			*title;
1116260e9a87SYuri Pankov 	size_t			 vollen, titlen;
111795c635efSGarrett D'Amore 
1118698f87a4SGarrett D'Amore 	assert(meta->title);
1119698f87a4SGarrett D'Amore 	assert(meta->msec);
112095c635efSGarrett D'Amore 
1121260e9a87SYuri Pankov 	volume = NULL == meta->vol ? "" : meta->vol;
1122260e9a87SYuri Pankov 	vollen = term_strlen(p, volume);
112395c635efSGarrett D'Amore 
112495c635efSGarrett D'Amore 	/* Top left corner: manual title and section. */
112595c635efSGarrett D'Amore 
1126260e9a87SYuri Pankov 	mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
112795c635efSGarrett D'Amore 	titlen = term_strlen(p, title);
112895c635efSGarrett D'Amore 
112995c635efSGarrett D'Amore 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1130698f87a4SGarrett D'Amore 	p->trailspace = 1;
113195c635efSGarrett D'Amore 	p->offset = 0;
1132260e9a87SYuri Pankov 	p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1133260e9a87SYuri Pankov 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1134260e9a87SYuri Pankov 	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
113595c635efSGarrett D'Amore 
113695c635efSGarrett D'Amore 	term_word(p, title);
113795c635efSGarrett D'Amore 	term_flushln(p);
113895c635efSGarrett D'Amore 
113995c635efSGarrett D'Amore 	/* At the top in the middle: manual volume. */
114095c635efSGarrett D'Amore 
114195c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
114295c635efSGarrett D'Amore 	p->offset = p->rmargin;
1143260e9a87SYuri Pankov 	p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
114495c635efSGarrett D'Amore 	    p->maxrmargin - titlen : p->maxrmargin;
114595c635efSGarrett D'Amore 
1146260e9a87SYuri Pankov 	term_word(p, volume);
114795c635efSGarrett D'Amore 	term_flushln(p);
114895c635efSGarrett D'Amore 
114995c635efSGarrett D'Amore 	/* Top right corner: title and section, again. */
115095c635efSGarrett D'Amore 
115195c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
1152698f87a4SGarrett D'Amore 	p->trailspace = 0;
115395c635efSGarrett D'Amore 	if (p->rmargin + titlen <= p->maxrmargin) {
115495c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
115595c635efSGarrett D'Amore 		p->offset = p->rmargin;
115695c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
115795c635efSGarrett D'Amore 		term_word(p, title);
115895c635efSGarrett D'Amore 		term_flushln(p);
115995c635efSGarrett D'Amore 	}
116095c635efSGarrett D'Amore 
116195c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOSPACE;
116295c635efSGarrett D'Amore 	p->offset = 0;
116395c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
116495c635efSGarrett D'Amore 
116595c635efSGarrett D'Amore 	/*
116695c635efSGarrett D'Amore 	 * Groff prints three blank lines before the content.
116795c635efSGarrett D'Amore 	 * Do the same, except in the temporary, undocumented
116895c635efSGarrett D'Amore 	 * mode imitating mdoc(7) output.
116995c635efSGarrett D'Amore 	 */
117095c635efSGarrett D'Amore 
117195c635efSGarrett D'Amore 	term_vspace(p);
117295c635efSGarrett D'Amore 	if ( ! p->mdocstyle) {
117395c635efSGarrett D'Amore 		term_vspace(p);
117495c635efSGarrett D'Amore 		term_vspace(p);
117595c635efSGarrett D'Amore 	}
1176260e9a87SYuri Pankov 	free(title);
117795c635efSGarrett D'Amore }
1178