xref: /illumos-gate/usr/src/cmd/mandoc/man_term.c (revision a40ea1a7d80eee1b409e9dcc2e48c730988147ea)
1*a40ea1a7SYuri Pankov /*	$Id: man_term.c,v 1.191 2017/02/15 14:10:08 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*a40ea1a7SYuri Pankov  * Copyright (c) 2010-2015, 2017 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  *
10371584c2SYuri 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
12371584c2SYuri 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"
30371584c2SYuri Pankov #include "mandoc.h"
31371584c2SYuri Pankov #include "roff.h"
3295c635efSGarrett D'Amore #include "man.h"
33371584c2SYuri 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, \
51371584c2SYuri Pankov 			  struct roff_node *n, \
52371584c2SYuri 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);
63371584c2SYuri Pankov static	void		  print_man_head(struct termp *,
64371584c2SYuri Pankov 				const struct roff_meta *);
65371584c2SYuri Pankov static	void		  print_man_foot(struct termp *,
66371584c2SYuri Pankov 				const struct roff_meta *);
6795c635efSGarrett D'Amore static	void		  print_bvspace(struct termp *,
68371584c2SYuri 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
141371584c2SYuri Pankov terminal_man(void *arg, const struct roff_man *man)
14295c635efSGarrett D'Amore {
14395c635efSGarrett D'Amore 	struct termp		*p;
144371584c2SYuri Pankov 	struct roff_node	*n;
14595c635efSGarrett D'Amore 	struct mtermp		 mt;
146*a40ea1a7SYuri Pankov 	size_t			 save_defindent;
14795c635efSGarrett D'Amore 
14895c635efSGarrett D'Amore 	p = (struct termp *)arg;
14995c635efSGarrett D'Amore 	p->overstep = 0;
150260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin = p->defrmargin;
15195c635efSGarrett D'Amore 	p->tabwidth = term_len(p, 5);
15295c635efSGarrett D'Amore 
15395c635efSGarrett D'Amore 	memset(&mt, 0, sizeof(struct mtermp));
15495c635efSGarrett D'Amore 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
15595c635efSGarrett D'Amore 	mt.offset = term_len(p, p->defindent);
156698f87a4SGarrett D'Amore 	mt.pardist = 1;
15795c635efSGarrett D'Amore 
158371584c2SYuri Pankov 	n = man->first->child;
159260e9a87SYuri Pankov 	if (p->synopsisonly) {
160260e9a87SYuri Pankov 		while (n != NULL) {
161260e9a87SYuri Pankov 			if (n->tok == MAN_SH &&
162371584c2SYuri Pankov 			    n->child->child->type == ROFFT_TEXT &&
163260e9a87SYuri Pankov 			    !strcmp(n->child->child->string, "SYNOPSIS")) {
164260e9a87SYuri Pankov 				if (n->child->next->child != NULL)
165260e9a87SYuri Pankov 					print_man_nodelist(p, &mt,
166371584c2SYuri Pankov 					    n->child->next->child,
167371584c2SYuri Pankov 					    &man->meta);
168260e9a87SYuri Pankov 				term_newln(p);
169260e9a87SYuri Pankov 				break;
170260e9a87SYuri Pankov 			}
171260e9a87SYuri Pankov 			n = n->next;
172260e9a87SYuri Pankov 		}
173260e9a87SYuri Pankov 	} else {
174*a40ea1a7SYuri Pankov 		save_defindent = p->defindent;
175260e9a87SYuri Pankov 		if (p->defindent == 0)
176260e9a87SYuri Pankov 			p->defindent = 7;
177371584c2SYuri Pankov 		term_begin(p, print_man_head, print_man_foot, &man->meta);
178260e9a87SYuri Pankov 		p->flags |= TERMP_NOSPACE;
179260e9a87SYuri Pankov 		if (n != NULL)
180371584c2SYuri Pankov 			print_man_nodelist(p, &mt, n, &man->meta);
18195c635efSGarrett D'Amore 		term_end(p);
182*a40ea1a7SYuri Pankov 		p->defindent = save_defindent;
18395c635efSGarrett D'Amore 	}
18495c635efSGarrett D'Amore }
18595c635efSGarrett D'Amore 
18695c635efSGarrett D'Amore /*
18795c635efSGarrett D'Amore  * Printing leading vertical space before a block.
18895c635efSGarrett D'Amore  * This is used for the paragraph macros.
18995c635efSGarrett D'Amore  * The rules are pretty simple, since there's very little nesting going
19095c635efSGarrett D'Amore  * on here.  Basically, if we're the first within another block (SS/SH),
19195c635efSGarrett D'Amore  * then don't emit vertical space.  If we are (RS), then do.  If not the
19295c635efSGarrett D'Amore  * first, print it.
19395c635efSGarrett D'Amore  */
19495c635efSGarrett D'Amore static void
195371584c2SYuri Pankov print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
19695c635efSGarrett D'Amore {
197698f87a4SGarrett D'Amore 	int	 i;
19895c635efSGarrett D'Amore 
19995c635efSGarrett D'Amore 	term_newln(p);
20095c635efSGarrett D'Amore 
20195c635efSGarrett D'Amore 	if (n->body && n->body->child)
202371584c2SYuri Pankov 		if (n->body->child->type == ROFFT_TBL)
20395c635efSGarrett D'Amore 			return;
20495c635efSGarrett D'Amore 
205371584c2SYuri Pankov 	if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
20695c635efSGarrett D'Amore 		if (NULL == n->prev)
20795c635efSGarrett D'Amore 			return;
20895c635efSGarrett D'Amore 
209698f87a4SGarrett D'Amore 	for (i = 0; i < pardist; i++)
21095c635efSGarrett D'Amore 		term_vspace(p);
21195c635efSGarrett D'Amore }
21295c635efSGarrett D'Amore 
213260e9a87SYuri Pankov 
21495c635efSGarrett D'Amore static int
21595c635efSGarrett D'Amore pre_ign(DECL_ARGS)
21695c635efSGarrett D'Amore {
21795c635efSGarrett D'Amore 
218371584c2SYuri Pankov 	return 0;
21995c635efSGarrett D'Amore }
22095c635efSGarrett D'Amore 
221260e9a87SYuri Pankov static int
222260e9a87SYuri Pankov pre_ll(DECL_ARGS)
223260e9a87SYuri Pankov {
22495c635efSGarrett D'Amore 
225371584c2SYuri Pankov 	term_setwidth(p, n->child != NULL ? n->child->string : NULL);
226371584c2SYuri Pankov 	return 0;
227260e9a87SYuri Pankov }
228260e9a87SYuri Pankov 
22995c635efSGarrett D'Amore static int
23095c635efSGarrett D'Amore pre_I(DECL_ARGS)
23195c635efSGarrett D'Amore {
23295c635efSGarrett D'Amore 
23395c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_UNDER);
234371584c2SYuri Pankov 	return 1;
23595c635efSGarrett D'Amore }
23695c635efSGarrett D'Amore 
23795c635efSGarrett D'Amore static int
23895c635efSGarrett D'Amore pre_literal(DECL_ARGS)
23995c635efSGarrett D'Amore {
24095c635efSGarrett D'Amore 
24195c635efSGarrett D'Amore 	term_newln(p);
24295c635efSGarrett D'Amore 
243698f87a4SGarrett D'Amore 	if (MAN_nf == n->tok || MAN_EX == n->tok)
24495c635efSGarrett D'Amore 		mt->fl |= MANT_LITERAL;
24595c635efSGarrett D'Amore 	else
24695c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
24795c635efSGarrett D'Amore 
24895c635efSGarrett D'Amore 	/*
24995c635efSGarrett D'Amore 	 * Unlike .IP and .TP, .HP does not have a HEAD.
25095c635efSGarrett D'Amore 	 * So in case a second call to term_flushln() is needed,
25195c635efSGarrett D'Amore 	 * indentation has to be set up explicitly.
25295c635efSGarrett D'Amore 	 */
25395c635efSGarrett D'Amore 	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
25495c635efSGarrett D'Amore 		p->offset = p->rmargin;
25595c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
256698f87a4SGarrett D'Amore 		p->trailspace = 0;
257260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
25895c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
25995c635efSGarrett D'Amore 	}
26095c635efSGarrett D'Amore 
261371584c2SYuri Pankov 	return 0;
26295c635efSGarrett D'Amore }
26395c635efSGarrett D'Amore 
26495c635efSGarrett D'Amore static int
265698f87a4SGarrett D'Amore pre_PD(DECL_ARGS)
266698f87a4SGarrett D'Amore {
267260e9a87SYuri Pankov 	struct roffsu	 su;
268698f87a4SGarrett D'Amore 
269698f87a4SGarrett D'Amore 	n = n->child;
270260e9a87SYuri Pankov 	if (n == NULL) {
271698f87a4SGarrett D'Amore 		mt->pardist = 1;
272371584c2SYuri Pankov 		return 0;
273698f87a4SGarrett D'Amore 	}
274371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
275260e9a87SYuri Pankov 	if (a2roffsu(n->string, &su, SCALE_VS))
276260e9a87SYuri Pankov 		mt->pardist = term_vspan(p, &su);
277371584c2SYuri Pankov 	return 0;
278698f87a4SGarrett D'Amore }
279698f87a4SGarrett D'Amore 
280698f87a4SGarrett D'Amore static int
28195c635efSGarrett D'Amore pre_alternate(DECL_ARGS)
28295c635efSGarrett D'Amore {
28395c635efSGarrett D'Amore 	enum termfont		 font[2];
284371584c2SYuri Pankov 	struct roff_node	*nn;
28595c635efSGarrett D'Amore 	int			 savelit, i;
28695c635efSGarrett D'Amore 
28795c635efSGarrett D'Amore 	switch (n->tok) {
288260e9a87SYuri Pankov 	case MAN_RB:
28995c635efSGarrett D'Amore 		font[0] = TERMFONT_NONE;
29095c635efSGarrett D'Amore 		font[1] = TERMFONT_BOLD;
29195c635efSGarrett D'Amore 		break;
292260e9a87SYuri Pankov 	case MAN_RI:
29395c635efSGarrett D'Amore 		font[0] = TERMFONT_NONE;
29495c635efSGarrett D'Amore 		font[1] = TERMFONT_UNDER;
29595c635efSGarrett D'Amore 		break;
296260e9a87SYuri Pankov 	case MAN_BR:
29795c635efSGarrett D'Amore 		font[0] = TERMFONT_BOLD;
29895c635efSGarrett D'Amore 		font[1] = TERMFONT_NONE;
29995c635efSGarrett D'Amore 		break;
300260e9a87SYuri Pankov 	case MAN_BI:
30195c635efSGarrett D'Amore 		font[0] = TERMFONT_BOLD;
30295c635efSGarrett D'Amore 		font[1] = TERMFONT_UNDER;
30395c635efSGarrett D'Amore 		break;
304260e9a87SYuri Pankov 	case MAN_IR:
30595c635efSGarrett D'Amore 		font[0] = TERMFONT_UNDER;
30695c635efSGarrett D'Amore 		font[1] = TERMFONT_NONE;
30795c635efSGarrett D'Amore 		break;
308260e9a87SYuri Pankov 	case MAN_IB:
30995c635efSGarrett D'Amore 		font[0] = TERMFONT_UNDER;
31095c635efSGarrett D'Amore 		font[1] = TERMFONT_BOLD;
31195c635efSGarrett D'Amore 		break;
31295c635efSGarrett D'Amore 	default:
31395c635efSGarrett D'Amore 		abort();
31495c635efSGarrett D'Amore 	}
31595c635efSGarrett D'Amore 
31695c635efSGarrett D'Amore 	savelit = MANT_LITERAL & mt->fl;
31795c635efSGarrett D'Amore 	mt->fl &= ~MANT_LITERAL;
31895c635efSGarrett D'Amore 
31995c635efSGarrett D'Amore 	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
32095c635efSGarrett D'Amore 		term_fontrepl(p, font[i]);
32195c635efSGarrett D'Amore 		if (savelit && NULL == nn->next)
32295c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
323371584c2SYuri Pankov 		assert(nn->type == ROFFT_TEXT);
324371584c2SYuri Pankov 		term_word(p, nn->string);
325*a40ea1a7SYuri Pankov 		if (nn->flags & NODE_EOS)
326371584c2SYuri Pankov                 	p->flags |= TERMP_SENTENCE;
32795c635efSGarrett D'Amore 		if (nn->next)
32895c635efSGarrett D'Amore 			p->flags |= TERMP_NOSPACE;
32995c635efSGarrett D'Amore 	}
33095c635efSGarrett D'Amore 
331371584c2SYuri Pankov 	return 0;
33295c635efSGarrett D'Amore }
33395c635efSGarrett D'Amore 
33495c635efSGarrett D'Amore static int
33595c635efSGarrett D'Amore pre_B(DECL_ARGS)
33695c635efSGarrett D'Amore {
33795c635efSGarrett D'Amore 
33895c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_BOLD);
339371584c2SYuri Pankov 	return 1;
34095c635efSGarrett D'Amore }
34195c635efSGarrett D'Amore 
34295c635efSGarrett D'Amore static int
34395c635efSGarrett D'Amore pre_OP(DECL_ARGS)
34495c635efSGarrett D'Amore {
34595c635efSGarrett D'Amore 
34695c635efSGarrett D'Amore 	term_word(p, "[");
34795c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
34895c635efSGarrett D'Amore 
34995c635efSGarrett D'Amore 	if (NULL != (n = n->child)) {
35095c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
35195c635efSGarrett D'Amore 		term_word(p, n->string);
35295c635efSGarrett D'Amore 	}
35395c635efSGarrett D'Amore 	if (NULL != n && NULL != n->next) {
35495c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_UNDER);
35595c635efSGarrett D'Amore 		term_word(p, n->next->string);
35695c635efSGarrett D'Amore 	}
35795c635efSGarrett D'Amore 
35895c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_NONE);
35995c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
36095c635efSGarrett D'Amore 	term_word(p, "]");
361371584c2SYuri Pankov 	return 0;
36295c635efSGarrett D'Amore }
36395c635efSGarrett D'Amore 
36495c635efSGarrett D'Amore static int
36595c635efSGarrett D'Amore pre_ft(DECL_ARGS)
36695c635efSGarrett D'Amore {
36795c635efSGarrett D'Amore 	const char	*cp;
36895c635efSGarrett D'Amore 
36995c635efSGarrett D'Amore 	if (NULL == n->child) {
37095c635efSGarrett D'Amore 		term_fontlast(p);
371371584c2SYuri Pankov 		return 0;
37295c635efSGarrett D'Amore 	}
37395c635efSGarrett D'Amore 
37495c635efSGarrett D'Amore 	cp = n->child->string;
37595c635efSGarrett D'Amore 	switch (*cp) {
376260e9a87SYuri Pankov 	case '4':
377260e9a87SYuri Pankov 	case '3':
378260e9a87SYuri Pankov 	case 'B':
37995c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
38095c635efSGarrett D'Amore 		break;
381260e9a87SYuri Pankov 	case '2':
382260e9a87SYuri Pankov 	case 'I':
38395c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_UNDER);
38495c635efSGarrett D'Amore 		break;
385260e9a87SYuri Pankov 	case 'P':
38695c635efSGarrett D'Amore 		term_fontlast(p);
38795c635efSGarrett D'Amore 		break;
388260e9a87SYuri Pankov 	case '1':
389260e9a87SYuri Pankov 	case 'C':
390260e9a87SYuri Pankov 	case 'R':
39195c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
39295c635efSGarrett D'Amore 		break;
39395c635efSGarrett D'Amore 	default:
39495c635efSGarrett D'Amore 		break;
39595c635efSGarrett D'Amore 	}
396371584c2SYuri Pankov 	return 0;
39795c635efSGarrett D'Amore }
39895c635efSGarrett D'Amore 
39995c635efSGarrett D'Amore static int
40095c635efSGarrett D'Amore pre_in(DECL_ARGS)
40195c635efSGarrett D'Amore {
402260e9a87SYuri Pankov 	struct roffsu	 su;
40395c635efSGarrett D'Amore 	const char	*cp;
404260e9a87SYuri Pankov 	size_t		 v;
405260e9a87SYuri Pankov 	int		 less;
40695c635efSGarrett D'Amore 
40795c635efSGarrett D'Amore 	term_newln(p);
40895c635efSGarrett D'Amore 
40995c635efSGarrett D'Amore 	if (NULL == n->child) {
41095c635efSGarrett D'Amore 		p->offset = mt->offset;
411371584c2SYuri Pankov 		return 0;
41295c635efSGarrett D'Amore 	}
41395c635efSGarrett D'Amore 
41495c635efSGarrett D'Amore 	cp = n->child->string;
41595c635efSGarrett D'Amore 	less = 0;
41695c635efSGarrett D'Amore 
41795c635efSGarrett D'Amore 	if ('-' == *cp)
41895c635efSGarrett D'Amore 		less = -1;
41995c635efSGarrett D'Amore 	else if ('+' == *cp)
42095c635efSGarrett D'Amore 		less = 1;
42195c635efSGarrett D'Amore 	else
42295c635efSGarrett D'Amore 		cp--;
42395c635efSGarrett D'Amore 
424260e9a87SYuri Pankov 	if ( ! a2roffsu(++cp, &su, SCALE_EN))
425371584c2SYuri Pankov 		return 0;
42695c635efSGarrett D'Amore 
427371584c2SYuri Pankov 	v = (term_hspan(p, &su) + 11) / 24;
42895c635efSGarrett D'Amore 
42995c635efSGarrett D'Amore 	if (less < 0)
43095c635efSGarrett D'Amore 		p->offset -= p->offset > v ? v : p->offset;
43195c635efSGarrett D'Amore 	else if (less > 0)
43295c635efSGarrett D'Amore 		p->offset += v;
43395c635efSGarrett D'Amore 	else
43495c635efSGarrett D'Amore 		p->offset = v;
435260e9a87SYuri Pankov 	if (p->offset > SHRT_MAX)
436260e9a87SYuri Pankov 		p->offset = term_len(p, p->defindent);
43795c635efSGarrett D'Amore 
438371584c2SYuri Pankov 	return 0;
43995c635efSGarrett D'Amore }
44095c635efSGarrett D'Amore 
44195c635efSGarrett D'Amore static int
44295c635efSGarrett D'Amore pre_sp(DECL_ARGS)
44395c635efSGarrett D'Amore {
444260e9a87SYuri Pankov 	struct roffsu	 su;
445260e9a87SYuri Pankov 	int		 i, len;
44695c635efSGarrett D'Amore 
44795c635efSGarrett D'Amore 	if ((NULL == n->prev && n->parent)) {
448698f87a4SGarrett D'Amore 		switch (n->parent->tok) {
449260e9a87SYuri Pankov 		case MAN_SH:
450260e9a87SYuri Pankov 		case MAN_SS:
451260e9a87SYuri Pankov 		case MAN_PP:
452260e9a87SYuri Pankov 		case MAN_LP:
453260e9a87SYuri Pankov 		case MAN_P:
454371584c2SYuri Pankov 			return 0;
455698f87a4SGarrett D'Amore 		default:
456698f87a4SGarrett D'Amore 			break;
457698f87a4SGarrett D'Amore 		}
45895c635efSGarrett D'Amore 	}
45995c635efSGarrett D'Amore 
460260e9a87SYuri Pankov 	if (n->tok == MAN_br)
46195c635efSGarrett D'Amore 		len = 0;
462260e9a87SYuri Pankov 	else if (n->child == NULL)
463698f87a4SGarrett D'Amore 		len = 1;
464260e9a87SYuri Pankov 	else {
465260e9a87SYuri Pankov 		if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
466260e9a87SYuri Pankov 			su.scale = 1.0;
467260e9a87SYuri Pankov 		len = term_vspan(p, &su);
46895c635efSGarrett D'Amore 	}
46995c635efSGarrett D'Amore 
470260e9a87SYuri Pankov 	if (len == 0)
47195c635efSGarrett D'Amore 		term_newln(p);
472260e9a87SYuri Pankov 	else if (len < 0)
473260e9a87SYuri Pankov 		p->skipvsp -= len;
474698f87a4SGarrett D'Amore 	else
47595c635efSGarrett D'Amore 		for (i = 0; i < len; i++)
47695c635efSGarrett D'Amore 			term_vspace(p);
47795c635efSGarrett D'Amore 
478371584c2SYuri Pankov 	/*
479371584c2SYuri Pankov 	 * Handle an explicit break request in the same way
480371584c2SYuri Pankov 	 * as an overflowing line.
481371584c2SYuri Pankov 	 */
482371584c2SYuri Pankov 
483371584c2SYuri Pankov 	if (p->flags & TERMP_BRIND) {
484371584c2SYuri Pankov 		p->offset = p->rmargin;
485371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
486371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
487371584c2SYuri Pankov 	}
488371584c2SYuri Pankov 
489371584c2SYuri Pankov 	return 0;
49095c635efSGarrett D'Amore }
49195c635efSGarrett D'Amore 
49295c635efSGarrett D'Amore static int
49395c635efSGarrett D'Amore pre_HP(DECL_ARGS)
49495c635efSGarrett D'Amore {
495260e9a87SYuri Pankov 	struct roffsu		 su;
496371584c2SYuri Pankov 	const struct roff_node	*nn;
497260e9a87SYuri Pankov 	int			 len;
49895c635efSGarrett D'Amore 
49995c635efSGarrett D'Amore 	switch (n->type) {
500371584c2SYuri Pankov 	case ROFFT_BLOCK:
501698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
502371584c2SYuri Pankov 		return 1;
503371584c2SYuri Pankov 	case ROFFT_BODY:
50495c635efSGarrett D'Amore 		break;
50595c635efSGarrett D'Amore 	default:
506371584c2SYuri Pankov 		return 0;
50795c635efSGarrett D'Amore 	}
50895c635efSGarrett D'Amore 
509698f87a4SGarrett D'Amore 	if ( ! (MANT_LITERAL & mt->fl)) {
510260e9a87SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
511698f87a4SGarrett D'Amore 		p->trailspace = 2;
512698f87a4SGarrett D'Amore 	}
513698f87a4SGarrett D'Amore 
51495c635efSGarrett D'Amore 	/* Calculate offset. */
51595c635efSGarrett D'Amore 
516260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
517260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
518371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
519260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
520260e9a87SYuri Pankov 			len = -mt->offset;
521260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
522260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
523260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
524260e9a87SYuri Pankov 	} else
525260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
52695c635efSGarrett D'Amore 
52795c635efSGarrett D'Amore 	p->offset = mt->offset;
52895c635efSGarrett D'Amore 	p->rmargin = mt->offset + len;
529371584c2SYuri Pankov 	return 1;
53095c635efSGarrett D'Amore }
53195c635efSGarrett D'Amore 
53295c635efSGarrett D'Amore static void
53395c635efSGarrett D'Amore post_HP(DECL_ARGS)
53495c635efSGarrett D'Amore {
53595c635efSGarrett D'Amore 
53695c635efSGarrett D'Amore 	switch (n->type) {
537371584c2SYuri Pankov 	case ROFFT_BODY:
538698f87a4SGarrett D'Amore 		term_newln(p);
539371584c2SYuri Pankov 
540371584c2SYuri Pankov 		/*
541371584c2SYuri Pankov 		 * Compatibility with a groff bug.
542371584c2SYuri Pankov 		 * The .HP macro uses the undocumented .tag request
543371584c2SYuri Pankov 		 * which causes a line break and cancels no-space
544371584c2SYuri Pankov 		 * mode even if there isn't any output.
545371584c2SYuri Pankov 		 */
546371584c2SYuri Pankov 
547371584c2SYuri Pankov 		if (n->child == NULL)
548371584c2SYuri Pankov 			term_vspace(p);
549371584c2SYuri Pankov 
550260e9a87SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
551698f87a4SGarrett D'Amore 		p->trailspace = 0;
55295c635efSGarrett D'Amore 		p->offset = mt->offset;
55395c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
55495c635efSGarrett D'Amore 		break;
55595c635efSGarrett D'Amore 	default:
55695c635efSGarrett D'Amore 		break;
55795c635efSGarrett D'Amore 	}
55895c635efSGarrett D'Amore }
55995c635efSGarrett D'Amore 
56095c635efSGarrett D'Amore static int
56195c635efSGarrett D'Amore pre_PP(DECL_ARGS)
56295c635efSGarrett D'Amore {
56395c635efSGarrett D'Amore 
56495c635efSGarrett D'Amore 	switch (n->type) {
565371584c2SYuri Pankov 	case ROFFT_BLOCK:
56695c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
567698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
56895c635efSGarrett D'Amore 		break;
56995c635efSGarrett D'Amore 	default:
57095c635efSGarrett D'Amore 		p->offset = mt->offset;
57195c635efSGarrett D'Amore 		break;
57295c635efSGarrett D'Amore 	}
57395c635efSGarrett D'Amore 
574371584c2SYuri Pankov 	return n->type != ROFFT_HEAD;
57595c635efSGarrett D'Amore }
57695c635efSGarrett D'Amore 
57795c635efSGarrett D'Amore static int
57895c635efSGarrett D'Amore pre_IP(DECL_ARGS)
57995c635efSGarrett D'Amore {
580260e9a87SYuri Pankov 	struct roffsu		 su;
581371584c2SYuri Pankov 	const struct roff_node	*nn;
582260e9a87SYuri Pankov 	int			 len, savelit;
58395c635efSGarrett D'Amore 
58495c635efSGarrett D'Amore 	switch (n->type) {
585371584c2SYuri Pankov 	case ROFFT_BODY:
58695c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
58795c635efSGarrett D'Amore 		break;
588371584c2SYuri Pankov 	case ROFFT_HEAD:
58995c635efSGarrett D'Amore 		p->flags |= TERMP_NOBREAK;
590698f87a4SGarrett D'Amore 		p->trailspace = 1;
59195c635efSGarrett D'Amore 		break;
592371584c2SYuri Pankov 	case ROFFT_BLOCK:
593698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
59495c635efSGarrett D'Amore 		/* FALLTHROUGH */
59595c635efSGarrett D'Amore 	default:
596371584c2SYuri Pankov 		return 1;
59795c635efSGarrett D'Amore 	}
59895c635efSGarrett D'Amore 
59995c635efSGarrett D'Amore 	/* Calculate the offset from the optional second argument. */
600260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
601260e9a87SYuri Pankov 	    (nn = nn->next) != NULL &&
602260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
603371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
604260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
605260e9a87SYuri Pankov 			len = -mt->offset;
606260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
607260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
608260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
609260e9a87SYuri Pankov 	} else
610260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
61195c635efSGarrett D'Amore 
61295c635efSGarrett D'Amore 	switch (n->type) {
613371584c2SYuri Pankov 	case ROFFT_HEAD:
61495c635efSGarrett D'Amore 		p->offset = mt->offset;
61595c635efSGarrett D'Amore 		p->rmargin = mt->offset + len;
61695c635efSGarrett D'Amore 
61795c635efSGarrett D'Amore 		savelit = MANT_LITERAL & mt->fl;
61895c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
61995c635efSGarrett D'Amore 
62095c635efSGarrett D'Amore 		if (n->child)
621698f87a4SGarrett D'Amore 			print_man_node(p, mt, n->child, meta);
62295c635efSGarrett D'Amore 
62395c635efSGarrett D'Amore 		if (savelit)
62495c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
62595c635efSGarrett D'Amore 
626371584c2SYuri Pankov 		return 0;
627371584c2SYuri Pankov 	case ROFFT_BODY:
62895c635efSGarrett D'Amore 		p->offset = mt->offset + len;
62995c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
63095c635efSGarrett D'Amore 		break;
63195c635efSGarrett D'Amore 	default:
63295c635efSGarrett D'Amore 		break;
63395c635efSGarrett D'Amore 	}
63495c635efSGarrett D'Amore 
635371584c2SYuri Pankov 	return 1;
63695c635efSGarrett D'Amore }
63795c635efSGarrett D'Amore 
63895c635efSGarrett D'Amore static void
63995c635efSGarrett D'Amore post_IP(DECL_ARGS)
64095c635efSGarrett D'Amore {
64195c635efSGarrett D'Amore 
64295c635efSGarrett D'Amore 	switch (n->type) {
643371584c2SYuri Pankov 	case ROFFT_HEAD:
64495c635efSGarrett D'Amore 		term_flushln(p);
64595c635efSGarrett D'Amore 		p->flags &= ~TERMP_NOBREAK;
646698f87a4SGarrett D'Amore 		p->trailspace = 0;
64795c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
64895c635efSGarrett D'Amore 		break;
649371584c2SYuri Pankov 	case ROFFT_BODY:
65095c635efSGarrett D'Amore 		term_newln(p);
651698f87a4SGarrett D'Amore 		p->offset = mt->offset;
65295c635efSGarrett D'Amore 		break;
65395c635efSGarrett D'Amore 	default:
65495c635efSGarrett D'Amore 		break;
65595c635efSGarrett D'Amore 	}
65695c635efSGarrett D'Amore }
65795c635efSGarrett D'Amore 
65895c635efSGarrett D'Amore static int
65995c635efSGarrett D'Amore pre_TP(DECL_ARGS)
66095c635efSGarrett D'Amore {
661260e9a87SYuri Pankov 	struct roffsu		 su;
662371584c2SYuri Pankov 	struct roff_node	*nn;
663260e9a87SYuri Pankov 	int			 len, savelit;
66495c635efSGarrett D'Amore 
66595c635efSGarrett D'Amore 	switch (n->type) {
666371584c2SYuri Pankov 	case ROFFT_HEAD:
667371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
668698f87a4SGarrett D'Amore 		p->trailspace = 1;
66995c635efSGarrett D'Amore 		break;
670371584c2SYuri Pankov 	case ROFFT_BODY:
67195c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
67295c635efSGarrett D'Amore 		break;
673371584c2SYuri Pankov 	case ROFFT_BLOCK:
674698f87a4SGarrett D'Amore 		print_bvspace(p, n, mt->pardist);
67595c635efSGarrett D'Amore 		/* FALLTHROUGH */
67695c635efSGarrett D'Amore 	default:
677371584c2SYuri Pankov 		return 1;
67895c635efSGarrett D'Amore 	}
67995c635efSGarrett D'Amore 
68095c635efSGarrett D'Amore 	/* Calculate offset. */
68195c635efSGarrett D'Amore 
682260e9a87SYuri Pankov 	if ((nn = n->parent->head->child) != NULL &&
683*a40ea1a7SYuri Pankov 	    nn->string != NULL && ! (NODE_LINE & nn->flags) &&
684260e9a87SYuri Pankov 	    a2roffsu(nn->string, &su, SCALE_EN)) {
685371584c2SYuri Pankov 		len = term_hspan(p, &su) / 24;
686260e9a87SYuri Pankov 		if (len < 0 && (size_t)(-len) > mt->offset)
687260e9a87SYuri Pankov 			len = -mt->offset;
688260e9a87SYuri Pankov 		else if (len > SHRT_MAX)
689260e9a87SYuri Pankov 			len = term_len(p, p->defindent);
690260e9a87SYuri Pankov 		mt->lmargin[mt->lmargincur] = len;
691260e9a87SYuri Pankov 	} else
692260e9a87SYuri Pankov 		len = mt->lmargin[mt->lmargincur];
69395c635efSGarrett D'Amore 
69495c635efSGarrett D'Amore 	switch (n->type) {
695371584c2SYuri Pankov 	case ROFFT_HEAD:
69695c635efSGarrett D'Amore 		p->offset = mt->offset;
69795c635efSGarrett D'Amore 		p->rmargin = mt->offset + len;
69895c635efSGarrett D'Amore 
69995c635efSGarrett D'Amore 		savelit = MANT_LITERAL & mt->fl;
70095c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
70195c635efSGarrett D'Amore 
70295c635efSGarrett D'Amore 		/* Don't print same-line elements. */
703260e9a87SYuri Pankov 		nn = n->child;
704*a40ea1a7SYuri Pankov 		while (NULL != nn && 0 == (NODE_LINE & nn->flags))
705260e9a87SYuri Pankov 			nn = nn->next;
706260e9a87SYuri Pankov 
707260e9a87SYuri Pankov 		while (NULL != nn) {
708698f87a4SGarrett D'Amore 			print_man_node(p, mt, nn, meta);
709260e9a87SYuri Pankov 			nn = nn->next;
710260e9a87SYuri Pankov 		}
71195c635efSGarrett D'Amore 
71295c635efSGarrett D'Amore 		if (savelit)
71395c635efSGarrett D'Amore 			mt->fl |= MANT_LITERAL;
714371584c2SYuri Pankov 		return 0;
715371584c2SYuri Pankov 	case ROFFT_BODY:
71695c635efSGarrett D'Amore 		p->offset = mt->offset + len;
71795c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
718698f87a4SGarrett D'Amore 		p->trailspace = 0;
719371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
72095c635efSGarrett D'Amore 		break;
72195c635efSGarrett D'Amore 	default:
72295c635efSGarrett D'Amore 		break;
72395c635efSGarrett D'Amore 	}
72495c635efSGarrett D'Amore 
725371584c2SYuri Pankov 	return 1;
72695c635efSGarrett D'Amore }
72795c635efSGarrett D'Amore 
72895c635efSGarrett D'Amore static void
72995c635efSGarrett D'Amore post_TP(DECL_ARGS)
73095c635efSGarrett D'Amore {
73195c635efSGarrett D'Amore 
73295c635efSGarrett D'Amore 	switch (n->type) {
733371584c2SYuri Pankov 	case ROFFT_HEAD:
73495c635efSGarrett D'Amore 		term_flushln(p);
73595c635efSGarrett D'Amore 		break;
736371584c2SYuri Pankov 	case ROFFT_BODY:
73795c635efSGarrett D'Amore 		term_newln(p);
738698f87a4SGarrett D'Amore 		p->offset = mt->offset;
73995c635efSGarrett D'Amore 		break;
74095c635efSGarrett D'Amore 	default:
74195c635efSGarrett D'Amore 		break;
74295c635efSGarrett D'Amore 	}
74395c635efSGarrett D'Amore }
74495c635efSGarrett D'Amore 
74595c635efSGarrett D'Amore static int
74695c635efSGarrett D'Amore pre_SS(DECL_ARGS)
74795c635efSGarrett D'Amore {
748698f87a4SGarrett D'Amore 	int	 i;
74995c635efSGarrett D'Amore 
75095c635efSGarrett D'Amore 	switch (n->type) {
751371584c2SYuri Pankov 	case ROFFT_BLOCK:
75295c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
75395c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
75495c635efSGarrett D'Amore 		mt->offset = term_len(p, p->defindent);
755260e9a87SYuri Pankov 
756260e9a87SYuri Pankov 		/*
757260e9a87SYuri Pankov 		 * No vertical space before the first subsection
758260e9a87SYuri Pankov 		 * and after an empty subsection.
759260e9a87SYuri Pankov 		 */
760260e9a87SYuri Pankov 
761260e9a87SYuri Pankov 		do {
762260e9a87SYuri Pankov 			n = n->prev;
763371584c2SYuri Pankov 		} while (n != NULL && n->tok != TOKEN_NONE &&
764371584c2SYuri Pankov 		    termacts[n->tok].flags & MAN_NOTEXT);
765260e9a87SYuri Pankov 		if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
76695c635efSGarrett D'Amore 			break;
767260e9a87SYuri Pankov 
768698f87a4SGarrett D'Amore 		for (i = 0; i < mt->pardist; i++)
76995c635efSGarrett D'Amore 			term_vspace(p);
77095c635efSGarrett D'Amore 		break;
771371584c2SYuri Pankov 	case ROFFT_HEAD:
77295c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
773698f87a4SGarrett D'Amore 		p->offset = term_len(p, 3);
774371584c2SYuri Pankov 		p->rmargin = mt->offset;
775371584c2SYuri Pankov 		p->trailspace = mt->offset;
776371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
77795c635efSGarrett D'Amore 		break;
778371584c2SYuri Pankov 	case ROFFT_BODY:
77995c635efSGarrett D'Amore 		p->offset = mt->offset;
780371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
781371584c2SYuri Pankov 		p->trailspace = 0;
782371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
78395c635efSGarrett D'Amore 		break;
78495c635efSGarrett D'Amore 	default:
78595c635efSGarrett D'Amore 		break;
78695c635efSGarrett D'Amore 	}
78795c635efSGarrett D'Amore 
788371584c2SYuri Pankov 	return 1;
78995c635efSGarrett D'Amore }
79095c635efSGarrett D'Amore 
79195c635efSGarrett D'Amore static void
79295c635efSGarrett D'Amore post_SS(DECL_ARGS)
79395c635efSGarrett D'Amore {
79495c635efSGarrett D'Amore 
79595c635efSGarrett D'Amore 	switch (n->type) {
796371584c2SYuri Pankov 	case ROFFT_HEAD:
79795c635efSGarrett D'Amore 		term_newln(p);
79895c635efSGarrett D'Amore 		break;
799371584c2SYuri Pankov 	case ROFFT_BODY:
80095c635efSGarrett D'Amore 		term_newln(p);
80195c635efSGarrett D'Amore 		break;
80295c635efSGarrett D'Amore 	default:
80395c635efSGarrett D'Amore 		break;
80495c635efSGarrett D'Amore 	}
80595c635efSGarrett D'Amore }
80695c635efSGarrett D'Amore 
80795c635efSGarrett D'Amore static int
80895c635efSGarrett D'Amore pre_SH(DECL_ARGS)
80995c635efSGarrett D'Amore {
810698f87a4SGarrett D'Amore 	int	 i;
81195c635efSGarrett D'Amore 
81295c635efSGarrett D'Amore 	switch (n->type) {
813371584c2SYuri Pankov 	case ROFFT_BLOCK:
81495c635efSGarrett D'Amore 		mt->fl &= ~MANT_LITERAL;
81595c635efSGarrett D'Amore 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
81695c635efSGarrett D'Amore 		mt->offset = term_len(p, p->defindent);
817260e9a87SYuri Pankov 
818260e9a87SYuri Pankov 		/*
819260e9a87SYuri Pankov 		 * No vertical space before the first section
820260e9a87SYuri Pankov 		 * and after an empty section.
821260e9a87SYuri Pankov 		 */
822260e9a87SYuri Pankov 
823260e9a87SYuri Pankov 		do {
824260e9a87SYuri Pankov 			n = n->prev;
825*a40ea1a7SYuri Pankov 		} while (n != NULL && n->tok != TOKEN_NONE &&
826*a40ea1a7SYuri Pankov 		    termacts[n->tok].flags & MAN_NOTEXT);
827260e9a87SYuri Pankov 		if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
82895c635efSGarrett D'Amore 			break;
829260e9a87SYuri Pankov 
830698f87a4SGarrett D'Amore 		for (i = 0; i < mt->pardist; i++)
83195c635efSGarrett D'Amore 			term_vspace(p);
83295c635efSGarrett D'Amore 		break;
833371584c2SYuri Pankov 	case ROFFT_HEAD:
83495c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_BOLD);
83595c635efSGarrett D'Amore 		p->offset = 0;
836371584c2SYuri Pankov 		p->rmargin = mt->offset;
837371584c2SYuri Pankov 		p->trailspace = mt->offset;
838371584c2SYuri Pankov 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
83995c635efSGarrett D'Amore 		break;
840371584c2SYuri Pankov 	case ROFFT_BODY:
84195c635efSGarrett D'Amore 		p->offset = mt->offset;
842371584c2SYuri Pankov 		p->rmargin = p->maxrmargin;
843371584c2SYuri Pankov 		p->trailspace = 0;
844371584c2SYuri Pankov 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
84595c635efSGarrett D'Amore 		break;
84695c635efSGarrett D'Amore 	default:
84795c635efSGarrett D'Amore 		break;
84895c635efSGarrett D'Amore 	}
84995c635efSGarrett D'Amore 
850371584c2SYuri Pankov 	return 1;
85195c635efSGarrett D'Amore }
85295c635efSGarrett D'Amore 
85395c635efSGarrett D'Amore static void
85495c635efSGarrett D'Amore post_SH(DECL_ARGS)
85595c635efSGarrett D'Amore {
85695c635efSGarrett D'Amore 
85795c635efSGarrett D'Amore 	switch (n->type) {
858371584c2SYuri Pankov 	case ROFFT_HEAD:
85995c635efSGarrett D'Amore 		term_newln(p);
86095c635efSGarrett D'Amore 		break;
861371584c2SYuri Pankov 	case ROFFT_BODY:
86295c635efSGarrett D'Amore 		term_newln(p);
86395c635efSGarrett D'Amore 		break;
86495c635efSGarrett D'Amore 	default:
86595c635efSGarrett D'Amore 		break;
86695c635efSGarrett D'Amore 	}
86795c635efSGarrett D'Amore }
86895c635efSGarrett D'Amore 
86995c635efSGarrett D'Amore static int
87095c635efSGarrett D'Amore pre_RS(DECL_ARGS)
87195c635efSGarrett D'Amore {
872260e9a87SYuri Pankov 	struct roffsu	 su;
87395c635efSGarrett D'Amore 
87495c635efSGarrett D'Amore 	switch (n->type) {
875371584c2SYuri Pankov 	case ROFFT_BLOCK:
87695c635efSGarrett D'Amore 		term_newln(p);
877371584c2SYuri Pankov 		return 1;
878371584c2SYuri Pankov 	case ROFFT_HEAD:
879371584c2SYuri Pankov 		return 0;
88095c635efSGarrett D'Amore 	default:
88195c635efSGarrett D'Amore 		break;
88295c635efSGarrett D'Amore 	}
88395c635efSGarrett D'Amore 
884260e9a87SYuri Pankov 	n = n->parent->head;
885260e9a87SYuri Pankov 	n->aux = SHRT_MAX + 1;
886371584c2SYuri Pankov 	if (n->child == NULL)
887371584c2SYuri Pankov 		n->aux = mt->lmargin[mt->lmargincur];
888371584c2SYuri Pankov 	else if (a2roffsu(n->child->string, &su, SCALE_EN))
889371584c2SYuri Pankov 		n->aux = term_hspan(p, &su) / 24;
890260e9a87SYuri Pankov 	if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
891260e9a87SYuri Pankov 		n->aux = -mt->offset;
892260e9a87SYuri Pankov 	else if (n->aux > SHRT_MAX)
893260e9a87SYuri Pankov 		n->aux = term_len(p, p->defindent);
89495c635efSGarrett D'Amore 
895260e9a87SYuri Pankov 	mt->offset += n->aux;
896260e9a87SYuri Pankov 	p->offset = mt->offset;
89795c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
89895c635efSGarrett D'Amore 
89995c635efSGarrett D'Amore 	if (++mt->lmarginsz < MAXMARGINS)
90095c635efSGarrett D'Amore 		mt->lmargincur = mt->lmarginsz;
90195c635efSGarrett D'Amore 
902371584c2SYuri Pankov 	mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
903371584c2SYuri Pankov 	return 1;
90495c635efSGarrett D'Amore }
90595c635efSGarrett D'Amore 
90695c635efSGarrett D'Amore static void
90795c635efSGarrett D'Amore post_RS(DECL_ARGS)
90895c635efSGarrett D'Amore {
90995c635efSGarrett D'Amore 
91095c635efSGarrett D'Amore 	switch (n->type) {
911371584c2SYuri Pankov 	case ROFFT_BLOCK:
91295c635efSGarrett D'Amore 		return;
913371584c2SYuri Pankov 	case ROFFT_HEAD:
91495c635efSGarrett D'Amore 		return;
91595c635efSGarrett D'Amore 	default:
91695c635efSGarrett D'Amore 		term_newln(p);
91795c635efSGarrett D'Amore 		break;
91895c635efSGarrett D'Amore 	}
91995c635efSGarrett D'Amore 
920260e9a87SYuri Pankov 	mt->offset -= n->parent->head->aux;
92195c635efSGarrett D'Amore 	p->offset = mt->offset;
92295c635efSGarrett D'Amore 
92395c635efSGarrett D'Amore 	if (--mt->lmarginsz < MAXMARGINS)
92495c635efSGarrett D'Amore 		mt->lmargincur = mt->lmarginsz;
92595c635efSGarrett D'Amore }
92695c635efSGarrett D'Amore 
927698f87a4SGarrett D'Amore static int
928698f87a4SGarrett D'Amore pre_UR(DECL_ARGS)
929698f87a4SGarrett D'Amore {
930698f87a4SGarrett D'Amore 
931371584c2SYuri Pankov 	return n->type != ROFFT_HEAD;
932698f87a4SGarrett D'Amore }
933698f87a4SGarrett D'Amore 
934698f87a4SGarrett D'Amore static void
935698f87a4SGarrett D'Amore post_UR(DECL_ARGS)
936698f87a4SGarrett D'Amore {
937698f87a4SGarrett D'Amore 
938371584c2SYuri Pankov 	if (n->type != ROFFT_BLOCK)
939698f87a4SGarrett D'Amore 		return;
940698f87a4SGarrett D'Amore 
941698f87a4SGarrett D'Amore 	term_word(p, "<");
942698f87a4SGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
943698f87a4SGarrett D'Amore 
944698f87a4SGarrett D'Amore 	if (NULL != n->child->child)
945698f87a4SGarrett D'Amore 		print_man_node(p, mt, n->child->child, meta);
946698f87a4SGarrett D'Amore 
947698f87a4SGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
948698f87a4SGarrett D'Amore 	term_word(p, ">");
949698f87a4SGarrett D'Amore }
950698f87a4SGarrett D'Amore 
95195c635efSGarrett D'Amore static void
95295c635efSGarrett D'Amore print_man_node(DECL_ARGS)
95395c635efSGarrett D'Amore {
95495c635efSGarrett D'Amore 	size_t		 rm, rmax;
95595c635efSGarrett D'Amore 	int		 c;
95695c635efSGarrett D'Amore 
95795c635efSGarrett D'Amore 	switch (n->type) {
958371584c2SYuri Pankov 	case ROFFT_TEXT:
95995c635efSGarrett D'Amore 		/*
96095c635efSGarrett D'Amore 		 * If we have a blank line, output a vertical space.
96195c635efSGarrett D'Amore 		 * If we have a space as the first character, break
96295c635efSGarrett D'Amore 		 * before printing the line's data.
96395c635efSGarrett D'Amore 		 */
96495c635efSGarrett D'Amore 		if ('\0' == *n->string) {
96595c635efSGarrett D'Amore 			term_vspace(p);
96695c635efSGarrett D'Amore 			return;
967*a40ea1a7SYuri Pankov 		} else if (' ' == *n->string && NODE_LINE & n->flags)
96895c635efSGarrett D'Amore 			term_newln(p);
96995c635efSGarrett D'Amore 
97095c635efSGarrett D'Amore 		term_word(p, n->string);
971698f87a4SGarrett D'Amore 		goto out;
97295c635efSGarrett D'Amore 
973371584c2SYuri Pankov 	case ROFFT_EQN:
974*a40ea1a7SYuri Pankov 		if ( ! (n->flags & NODE_LINE))
975260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
97695c635efSGarrett D'Amore 		term_eqn(p, n->eqn);
977*a40ea1a7SYuri Pankov 		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
978260e9a87SYuri Pankov 			p->flags |= TERMP_NOSPACE;
97995c635efSGarrett D'Amore 		return;
980371584c2SYuri Pankov 	case ROFFT_TBL:
981260e9a87SYuri Pankov 		if (p->tbl.cols == NULL)
982260e9a87SYuri Pankov 			term_vspace(p);
98395c635efSGarrett D'Amore 		term_tbl(p, n->span);
98495c635efSGarrett D'Amore 		return;
98595c635efSGarrett D'Amore 	default:
98695c635efSGarrett D'Amore 		break;
98795c635efSGarrett D'Amore 	}
98895c635efSGarrett D'Amore 
98995c635efSGarrett D'Amore 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
99095c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
99195c635efSGarrett D'Amore 
99295c635efSGarrett D'Amore 	c = 1;
99395c635efSGarrett D'Amore 	if (termacts[n->tok].pre)
994698f87a4SGarrett D'Amore 		c = (*termacts[n->tok].pre)(p, mt, n, meta);
99595c635efSGarrett D'Amore 
99695c635efSGarrett D'Amore 	if (c && n->child)
997698f87a4SGarrett D'Amore 		print_man_nodelist(p, mt, n->child, meta);
99895c635efSGarrett D'Amore 
99995c635efSGarrett D'Amore 	if (termacts[n->tok].post)
1000698f87a4SGarrett D'Amore 		(*termacts[n->tok].post)(p, mt, n, meta);
100195c635efSGarrett D'Amore 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
100295c635efSGarrett D'Amore 		term_fontrepl(p, TERMFONT_NONE);
100395c635efSGarrett D'Amore 
1004698f87a4SGarrett D'Amore out:
1005698f87a4SGarrett D'Amore 	/*
1006698f87a4SGarrett D'Amore 	 * If we're in a literal context, make sure that words
1007698f87a4SGarrett D'Amore 	 * together on the same line stay together.  This is a
1008698f87a4SGarrett D'Amore 	 * POST-printing call, so we check the NEXT word.  Since
1009698f87a4SGarrett D'Amore 	 * -man doesn't have nested macros, we don't need to be
1010698f87a4SGarrett D'Amore 	 * more specific than this.
1011698f87a4SGarrett D'Amore 	 */
1012260e9a87SYuri Pankov 	if (mt->fl & MANT_LITERAL &&
1013260e9a87SYuri Pankov 	    ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1014*a40ea1a7SYuri Pankov 	    (n->next == NULL || n->next->flags & NODE_LINE)) {
1015698f87a4SGarrett D'Amore 		rm = p->rmargin;
1016698f87a4SGarrett D'Amore 		rmax = p->maxrmargin;
1017698f87a4SGarrett D'Amore 		p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1018698f87a4SGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
1019260e9a87SYuri Pankov 		if (n->string != NULL && *n->string != '\0')
1020698f87a4SGarrett D'Amore 			term_flushln(p);
1021698f87a4SGarrett D'Amore 		else
1022698f87a4SGarrett D'Amore 			term_newln(p);
1023698f87a4SGarrett D'Amore 		if (rm < rmax && n->parent->tok == MAN_HP) {
1024698f87a4SGarrett D'Amore 			p->offset = rm;
1025698f87a4SGarrett D'Amore 			p->rmargin = rmax;
1026698f87a4SGarrett D'Amore 		} else
1027698f87a4SGarrett D'Amore 			p->rmargin = rm;
1028698f87a4SGarrett D'Amore 		p->maxrmargin = rmax;
1029698f87a4SGarrett D'Amore 	}
1030*a40ea1a7SYuri Pankov 	if (NODE_EOS & n->flags)
103195c635efSGarrett D'Amore 		p->flags |= TERMP_SENTENCE;
103295c635efSGarrett D'Amore }
103395c635efSGarrett D'Amore 
103495c635efSGarrett D'Amore 
103595c635efSGarrett D'Amore static void
103695c635efSGarrett D'Amore print_man_nodelist(DECL_ARGS)
103795c635efSGarrett D'Amore {
103895c635efSGarrett D'Amore 
1039260e9a87SYuri Pankov 	while (n != NULL) {
1040698f87a4SGarrett D'Amore 		print_man_node(p, mt, n, meta);
1041260e9a87SYuri Pankov 		n = n->next;
104295c635efSGarrett D'Amore 	}
1043260e9a87SYuri Pankov }
104495c635efSGarrett D'Amore 
104595c635efSGarrett D'Amore static void
1046371584c2SYuri Pankov print_man_foot(struct termp *p, const struct roff_meta *meta)
104795c635efSGarrett D'Amore {
1048260e9a87SYuri Pankov 	char			*title;
1049260e9a87SYuri Pankov 	size_t			 datelen, titlen;
105095c635efSGarrett D'Amore 
105195c635efSGarrett D'Amore 	assert(meta->title);
105295c635efSGarrett D'Amore 	assert(meta->msec);
105395c635efSGarrett D'Amore 	assert(meta->date);
105495c635efSGarrett D'Amore 
105595c635efSGarrett D'Amore 	term_fontrepl(p, TERMFONT_NONE);
105695c635efSGarrett D'Amore 
1057260e9a87SYuri Pankov 	if (meta->hasbody)
105895c635efSGarrett D'Amore 		term_vspace(p);
105995c635efSGarrett D'Amore 
106095c635efSGarrett D'Amore 	/*
106195c635efSGarrett D'Amore 	 * Temporary, undocumented option to imitate mdoc(7) output.
1062371584c2SYuri Pankov 	 * In the bottom right corner, use the operating system
1063371584c2SYuri Pankov 	 * instead of the title.
106495c635efSGarrett D'Amore 	 */
106595c635efSGarrett D'Amore 
106695c635efSGarrett D'Amore 	if ( ! p->mdocstyle) {
1067260e9a87SYuri Pankov 		if (meta->hasbody) {
106895c635efSGarrett D'Amore 			term_vspace(p);
106995c635efSGarrett D'Amore 			term_vspace(p);
1070260e9a87SYuri Pankov 		}
1071260e9a87SYuri Pankov 		mandoc_asprintf(&title, "%s(%s)",
1072260e9a87SYuri Pankov 		    meta->title, meta->msec);
1073371584c2SYuri Pankov 	} else if (meta->os) {
1074371584c2SYuri Pankov 		title = mandoc_strdup(meta->os);
107595c635efSGarrett D'Amore 	} else {
1076260e9a87SYuri Pankov 		title = mandoc_strdup("");
107795c635efSGarrett D'Amore 	}
107895c635efSGarrett D'Amore 	datelen = term_strlen(p, meta->date);
107995c635efSGarrett D'Amore 
1080371584c2SYuri Pankov 	/* Bottom left corner: operating system. */
108195c635efSGarrett D'Amore 
108295c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1083698f87a4SGarrett D'Amore 	p->trailspace = 1;
108495c635efSGarrett D'Amore 	p->offset = 0;
1085260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin > datelen ?
1086260e9a87SYuri Pankov 	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
108795c635efSGarrett D'Amore 
1088371584c2SYuri Pankov 	if (meta->os)
1089371584c2SYuri Pankov 		term_word(p, meta->os);
109095c635efSGarrett D'Amore 	term_flushln(p);
109195c635efSGarrett D'Amore 
109295c635efSGarrett D'Amore 	/* At the bottom in the middle: manual date. */
109395c635efSGarrett D'Amore 
109495c635efSGarrett D'Amore 	p->offset = p->rmargin;
1095260e9a87SYuri Pankov 	titlen = term_strlen(p, title);
1096260e9a87SYuri Pankov 	p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
1097260e9a87SYuri Pankov 	p->flags |= TERMP_NOSPACE;
109895c635efSGarrett D'Amore 
109995c635efSGarrett D'Amore 	term_word(p, meta->date);
110095c635efSGarrett D'Amore 	term_flushln(p);
110195c635efSGarrett D'Amore 
110295c635efSGarrett D'Amore 	/* Bottom right corner: manual title and section. */
110395c635efSGarrett D'Amore 
110495c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
110595c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
1106698f87a4SGarrett D'Amore 	p->trailspace = 0;
110795c635efSGarrett D'Amore 	p->offset = p->rmargin;
110895c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
110995c635efSGarrett D'Amore 
111095c635efSGarrett D'Amore 	term_word(p, title);
111195c635efSGarrett D'Amore 	term_flushln(p);
1112260e9a87SYuri Pankov 	free(title);
111395c635efSGarrett D'Amore }
111495c635efSGarrett D'Amore 
111595c635efSGarrett D'Amore static void
1116371584c2SYuri Pankov print_man_head(struct termp *p, const struct roff_meta *meta)
111795c635efSGarrett D'Amore {
1118260e9a87SYuri Pankov 	const char		*volume;
1119260e9a87SYuri Pankov 	char			*title;
1120260e9a87SYuri Pankov 	size_t			 vollen, titlen;
112195c635efSGarrett D'Amore 
1122698f87a4SGarrett D'Amore 	assert(meta->title);
1123698f87a4SGarrett D'Amore 	assert(meta->msec);
112495c635efSGarrett D'Amore 
1125260e9a87SYuri Pankov 	volume = NULL == meta->vol ? "" : meta->vol;
1126260e9a87SYuri Pankov 	vollen = term_strlen(p, volume);
112795c635efSGarrett D'Amore 
112895c635efSGarrett D'Amore 	/* Top left corner: manual title and section. */
112995c635efSGarrett D'Amore 
1130260e9a87SYuri Pankov 	mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
113195c635efSGarrett D'Amore 	titlen = term_strlen(p, title);
113295c635efSGarrett D'Amore 
113395c635efSGarrett D'Amore 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1134698f87a4SGarrett D'Amore 	p->trailspace = 1;
113595c635efSGarrett D'Amore 	p->offset = 0;
1136260e9a87SYuri Pankov 	p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1137260e9a87SYuri Pankov 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1138260e9a87SYuri Pankov 	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
113995c635efSGarrett D'Amore 
114095c635efSGarrett D'Amore 	term_word(p, title);
114195c635efSGarrett D'Amore 	term_flushln(p);
114295c635efSGarrett D'Amore 
114395c635efSGarrett D'Amore 	/* At the top in the middle: manual volume. */
114495c635efSGarrett D'Amore 
114595c635efSGarrett D'Amore 	p->flags |= TERMP_NOSPACE;
114695c635efSGarrett D'Amore 	p->offset = p->rmargin;
1147260e9a87SYuri Pankov 	p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
114895c635efSGarrett D'Amore 	    p->maxrmargin - titlen : p->maxrmargin;
114995c635efSGarrett D'Amore 
1150260e9a87SYuri Pankov 	term_word(p, volume);
115195c635efSGarrett D'Amore 	term_flushln(p);
115295c635efSGarrett D'Amore 
115395c635efSGarrett D'Amore 	/* Top right corner: title and section, again. */
115495c635efSGarrett D'Amore 
115595c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOBREAK;
1156698f87a4SGarrett D'Amore 	p->trailspace = 0;
115795c635efSGarrett D'Amore 	if (p->rmargin + titlen <= p->maxrmargin) {
115895c635efSGarrett D'Amore 		p->flags |= TERMP_NOSPACE;
115995c635efSGarrett D'Amore 		p->offset = p->rmargin;
116095c635efSGarrett D'Amore 		p->rmargin = p->maxrmargin;
116195c635efSGarrett D'Amore 		term_word(p, title);
116295c635efSGarrett D'Amore 		term_flushln(p);
116395c635efSGarrett D'Amore 	}
116495c635efSGarrett D'Amore 
116595c635efSGarrett D'Amore 	p->flags &= ~TERMP_NOSPACE;
116695c635efSGarrett D'Amore 	p->offset = 0;
116795c635efSGarrett D'Amore 	p->rmargin = p->maxrmargin;
116895c635efSGarrett D'Amore 
116995c635efSGarrett D'Amore 	/*
117095c635efSGarrett D'Amore 	 * Groff prints three blank lines before the content.
117195c635efSGarrett D'Amore 	 * Do the same, except in the temporary, undocumented
117295c635efSGarrett D'Amore 	 * mode imitating mdoc(7) output.
117395c635efSGarrett D'Amore 	 */
117495c635efSGarrett D'Amore 
117595c635efSGarrett D'Amore 	term_vspace(p);
117695c635efSGarrett D'Amore 	if ( ! p->mdocstyle) {
117795c635efSGarrett D'Amore 		term_vspace(p);
117895c635efSGarrett D'Amore 		term_vspace(p);
117995c635efSGarrett D'Amore 	}
1180260e9a87SYuri Pankov 	free(title);
118195c635efSGarrett D'Amore }
1182