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