1*84680f53Sschwarze /* $OpenBSD: man_term.c,v 1.197 2023/11/13 19:13:00 schwarze Exp $ */
2f73abda9Skristaps /*
3f6697133Sschwarze * Copyright (c) 2010-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
40ac7e6ecSschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
5f73abda9Skristaps *
6f73abda9Skristaps * Permission to use, copy, modify, and distribute this software for any
7a6464425Sschwarze * purpose with or without fee is hereby granted, provided that the above
8a6464425Sschwarze * copyright notice and this permission notice appear in all copies.
9f73abda9Skristaps *
10d1982c71Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11a6464425Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d1982c71Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13a6464425Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a6464425Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a6464425Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a6464425Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170ac7e6ecSschwarze *
180ac7e6ecSschwarze * Plain text formatter for man(7), used by mandoc(1)
190ac7e6ecSschwarze * for ASCII, UTF-8, PostScript, and PDF output.
20f73abda9Skristaps */
21fbaebbd2Sschwarze #include <sys/types.h>
22fbaebbd2Sschwarze
23f73abda9Skristaps #include <assert.h>
24e35cb253Sschwarze #include <ctype.h>
259327b421Sschwarze #include <limits.h>
26f73abda9Skristaps #include <stdio.h>
27f73abda9Skristaps #include <stdlib.h>
28f73abda9Skristaps #include <string.h>
29f73abda9Skristaps
30a92c1cd8Sschwarze #include "mandoc_aux.h"
311e876328Sschwarze #include "mandoc.h"
32d1982c71Sschwarze #include "roff.h"
33f73abda9Skristaps #include "man.h"
34d1982c71Sschwarze #include "out.h"
354175bdabSschwarze #include "term.h"
360ac7e6ecSschwarze #include "term_tag.h"
374175bdabSschwarze #include "main.h"
38f73abda9Skristaps
396330f331Sschwarze #define MAXMARGINS 64 /* maximum number of indented scopes */
408c3b1337Sschwarze
41e35cb253Sschwarze struct mtermp {
42bf8e53c9Sschwarze int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
436330f331Sschwarze int lmargincur; /* index of current margin */
446330f331Sschwarze int lmarginsz; /* actual number of nested margins */
456330f331Sschwarze size_t offset; /* default offset to visible page */
46ffeff720Sschwarze int pardist; /* vert. space before par., unit: [v] */
47e35cb253Sschwarze };
48e35cb253Sschwarze
49f73abda9Skristaps #define DECL_ARGS struct termp *p, \
50e35cb253Sschwarze struct mtermp *mt, \
513a0d07afSschwarze struct roff_node *n, \
522a238f45Sschwarze const struct roff_meta *meta
53f73abda9Skristaps
5416fe0cfcSschwarze struct man_term_act {
55f73abda9Skristaps int (*pre)(DECL_ARGS);
56f73abda9Skristaps void (*post)(DECL_ARGS);
5787e50d6cSschwarze int flags;
5887e50d6cSschwarze #define MAN_NOTEXT (1 << 0) /* Never has text children. */
59f73abda9Skristaps };
60f73abda9Skristaps
61fa70b73eSschwarze static void print_man_nodelist(DECL_ARGS);
62b822ca0dSschwarze static void print_man_node(DECL_ARGS);
632a238f45Sschwarze static void print_man_head(struct termp *,
642a238f45Sschwarze const struct roff_meta *);
652a238f45Sschwarze static void print_man_foot(struct termp *,
662a238f45Sschwarze const struct roff_meta *);
674175bdabSschwarze static void print_bvspace(struct termp *,
687ebbefbeSschwarze struct roff_node *, int);
694175bdabSschwarze
70f73abda9Skristaps static int pre_B(DECL_ARGS);
718ea764d3Sschwarze static int pre_DT(DECL_ARGS);
72e35cb253Sschwarze static int pre_HP(DECL_ARGS);
73f73abda9Skristaps static int pre_I(DECL_ARGS);
74f73abda9Skristaps static int pre_IP(DECL_ARGS);
75f6697133Sschwarze static int pre_MR(DECL_ARGS);
7666ae7cc0Sschwarze static int pre_OP(DECL_ARGS);
77ffeff720Sschwarze static int pre_PD(DECL_ARGS);
78f73abda9Skristaps static int pre_PP(DECL_ARGS);
79fbaebbd2Sschwarze static int pre_RS(DECL_ARGS);
80f73abda9Skristaps static int pre_SH(DECL_ARGS);
81f73abda9Skristaps static int pre_SS(DECL_ARGS);
825e5a9c61Sschwarze static int pre_SY(DECL_ARGS);
83f73abda9Skristaps static int pre_TP(DECL_ARGS);
843aeff926Sschwarze static int pre_UR(DECL_ARGS);
8566ae7cc0Sschwarze static int pre_alternate(DECL_ARGS);
860b55bec8Sschwarze static int pre_ign(DECL_ARGS);
87ddce0b0cSschwarze static int pre_in(DECL_ARGS);
88ddce0b0cSschwarze static int pre_literal(DECL_ARGS);
89f73abda9Skristaps
90e35cb253Sschwarze static void post_IP(DECL_ARGS);
91e35cb253Sschwarze static void post_HP(DECL_ARGS);
92fbaebbd2Sschwarze static void post_RS(DECL_ARGS);
93f73abda9Skristaps static void post_SH(DECL_ARGS);
945e5a9c61Sschwarze static void post_SY(DECL_ARGS);
95e35cb253Sschwarze static void post_TP(DECL_ARGS);
963aeff926Sschwarze static void post_UR(DECL_ARGS);
97f73abda9Skristaps
9816fe0cfcSschwarze static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
9987e50d6cSschwarze { NULL, NULL, 0 }, /* TH */
10087e50d6cSschwarze { pre_SH, post_SH, 0 }, /* SH */
10193d668c7Sschwarze { pre_SS, post_SH, 0 }, /* SS */
10287e50d6cSschwarze { pre_TP, post_TP, 0 }, /* TP */
103d991fc2cSschwarze { pre_TP, post_TP, 0 }, /* TQ */
1043f3c303aSschwarze { pre_PP, NULL, 0 }, /* LP */
10587e50d6cSschwarze { pre_PP, NULL, 0 }, /* PP */
1063f3c303aSschwarze { pre_PP, NULL, 0 }, /* P */
10787e50d6cSschwarze { pre_IP, post_IP, 0 }, /* IP */
10887e50d6cSschwarze { pre_HP, post_HP, 0 }, /* HP */
10987e50d6cSschwarze { NULL, NULL, 0 }, /* SM */
11087e50d6cSschwarze { pre_B, NULL, 0 }, /* SB */
111a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* BI */
112a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* IB */
113a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* BR */
114a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* RB */
11587e50d6cSschwarze { NULL, NULL, 0 }, /* R */
11687e50d6cSschwarze { pre_B, NULL, 0 }, /* B */
11787e50d6cSschwarze { pre_I, NULL, 0 }, /* I */
118a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* IR */
119a2709d4eSschwarze { pre_alternate, NULL, 0 }, /* RI */
12087e50d6cSschwarze { NULL, NULL, 0 }, /* RE */
12187e50d6cSschwarze { pre_RS, post_RS, 0 }, /* RS */
1221ad7d38cSschwarze { pre_DT, NULL, MAN_NOTEXT }, /* DT */
1235b8aa33bSschwarze { pre_ign, NULL, MAN_NOTEXT }, /* UC */
124ffeff720Sschwarze { pre_PD, NULL, MAN_NOTEXT }, /* PD */
1251ad7d38cSschwarze { pre_ign, NULL, MAN_NOTEXT }, /* AT */
126ddce0b0cSschwarze { pre_in, NULL, MAN_NOTEXT }, /* in */
1275e5a9c61Sschwarze { pre_SY, post_SY, 0 }, /* SY */
1285e5a9c61Sschwarze { NULL, NULL, 0 }, /* YS */
12966ae7cc0Sschwarze { pre_OP, NULL, 0 }, /* OP */
1308d0fa220Sschwarze { pre_literal, NULL, 0 }, /* EX */
1318d0fa220Sschwarze { pre_literal, NULL, 0 }, /* EE */
1323aeff926Sschwarze { pre_UR, post_UR, 0 }, /* UR */
1333aeff926Sschwarze { NULL, NULL, 0 }, /* UE */
134df9a9479Sbentley { pre_UR, post_UR, 0 }, /* MT */
135df9a9479Sbentley { NULL, NULL, 0 }, /* ME */
136f6697133Sschwarze { pre_MR, NULL, 0 }, /* MR */
137f73abda9Skristaps };
13816fe0cfcSschwarze static const struct man_term_act *man_term_act(enum roff_tok);
139f73abda9Skristaps
140f73abda9Skristaps
14116fe0cfcSschwarze static const struct man_term_act *
man_term_act(enum roff_tok tok)14216fe0cfcSschwarze man_term_act(enum roff_tok tok)
14316fe0cfcSschwarze {
14416fe0cfcSschwarze assert(tok >= MAN_TH && tok <= MAN_MAX);
14516fe0cfcSschwarze return man_term_acts + (tok - MAN_TH);
14616fe0cfcSschwarze }
14716fe0cfcSschwarze
1486ae2e8acSschwarze void
terminal_man(void * arg,const struct roff_meta * man)1496b86842eSschwarze terminal_man(void *arg, const struct roff_meta *man)
150f73abda9Skristaps {
15193d668c7Sschwarze struct mtermp mt;
1524175bdabSschwarze struct termp *p;
153af29ff23Sschwarze struct roff_node *n, *nc, *nn;
154f73abda9Skristaps
1554175bdabSschwarze p = (struct termp *)arg;
156e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin = p->defrmargin;
157f7242c43Sschwarze term_tab_set(p, NULL);
158f7242c43Sschwarze term_tab_set(p, "T");
159f7242c43Sschwarze term_tab_set(p, ".5i");
160ac531cf1Sschwarze
16193d668c7Sschwarze memset(&mt, 0, sizeof(mt));
162*84680f53Sschwarze mt.lmargin[mt.lmargincur] = term_len(p, 7);
163e12fe158Sschwarze mt.offset = term_len(p, p->defindent);
164ffeff720Sschwarze mt.pardist = 1;
165e35cb253Sschwarze
1666d0e9b63Sschwarze n = man->first->child;
1670a0199c7Sschwarze if (p->synopsisonly) {
168af29ff23Sschwarze for (nn = NULL; n != NULL; n = n->next) {
169af29ff23Sschwarze if (n->tok != MAN_SH)
170af29ff23Sschwarze continue;
171af29ff23Sschwarze nc = n->child->child;
172af29ff23Sschwarze if (nc->type != ROFFT_TEXT)
173af29ff23Sschwarze continue;
174af29ff23Sschwarze if (strcmp(nc->string, "SYNOPSIS") == 0)
1750a0199c7Sschwarze break;
176af29ff23Sschwarze if (nn == NULL && strcmp(nc->string, "NAME") == 0)
177af29ff23Sschwarze nn = n;
1780a0199c7Sschwarze }
179af29ff23Sschwarze if (n == NULL)
180af29ff23Sschwarze n = nn;
181af29ff23Sschwarze p->flags |= TERMP_NOSPACE;
182af29ff23Sschwarze if (n != NULL && (n = n->child->next->child) != NULL)
183af29ff23Sschwarze print_man_nodelist(p, &mt, n, man);
184af29ff23Sschwarze term_newln(p);
1850a0199c7Sschwarze } else {
1866b86842eSschwarze term_begin(p, print_man_head, print_man_foot, man);
1870a0199c7Sschwarze p->flags |= TERMP_NOSPACE;
1880a0199c7Sschwarze if (n != NULL)
1896b86842eSschwarze print_man_nodelist(p, &mt, n, man);
190f95d589eSschwarze term_end(p);
1914175bdabSschwarze }
1920a0199c7Sschwarze }
1934175bdabSschwarze
194296801eeSschwarze /*
195296801eeSschwarze * Printing leading vertical space before a block.
196296801eeSschwarze * This is used for the paragraph macros.
197296801eeSschwarze * The rules are pretty simple, since there's very little nesting going
198296801eeSschwarze * on here. Basically, if we're the first within another block (SS/SH),
199296801eeSschwarze * then don't emit vertical space. If we are (RS), then do. If not the
200296801eeSschwarze * first, print it.
201296801eeSschwarze */
202e35cb253Sschwarze static void
print_bvspace(struct termp * p,struct roff_node * n,int pardist)2037ebbefbeSschwarze print_bvspace(struct termp *p, struct roff_node *n, int pardist)
204e35cb253Sschwarze {
2057ebbefbeSschwarze struct roff_node *nch;
206ffeff720Sschwarze int i;
207296801eeSschwarze
208e35cb253Sschwarze term_newln(p);
209e35cb253Sschwarze
2107ebbefbeSschwarze if (n->body != NULL &&
2117ebbefbeSschwarze (nch = roff_node_child(n->body)) != NULL &&
2127ebbefbeSschwarze nch->type == ROFFT_TBL)
213c5ee43dcSschwarze return;
214c5ee43dcSschwarze
2157ebbefbeSschwarze if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL)
216e35cb253Sschwarze return;
217e35cb253Sschwarze
218ffeff720Sschwarze for (i = 0; i < pardist; i++)
219e35cb253Sschwarze term_vspace(p);
220e35cb253Sschwarze }
221e35cb253Sschwarze
2227c539ecbSschwarze static int
pre_ign(DECL_ARGS)2230b55bec8Sschwarze pre_ign(DECL_ARGS)
2240b55bec8Sschwarze {
225526e306bSschwarze return 0;
2260b55bec8Sschwarze }
2270b55bec8Sschwarze
2280b55bec8Sschwarze static int
pre_I(DECL_ARGS)229f73abda9Skristaps pre_I(DECL_ARGS)
230f73abda9Skristaps {
231fa70b73eSschwarze term_fontrepl(p, TERMFONT_UNDER);
232526e306bSschwarze return 1;
233f73abda9Skristaps }
234f73abda9Skristaps
235e35cb253Sschwarze static int
pre_literal(DECL_ARGS)236ddce0b0cSschwarze pre_literal(DECL_ARGS)
237e35cb253Sschwarze {
238ddce0b0cSschwarze term_newln(p);
23967a859d6Sschwarze
2406b4eabafSschwarze /*
2416b4eabafSschwarze * Unlike .IP and .TP, .HP does not have a HEAD.
2426b4eabafSschwarze * So in case a second call to term_flushln() is needed,
2436b4eabafSschwarze * indentation has to be set up explicitly.
2446b4eabafSschwarze */
245e93ea447Sschwarze if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
246e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
247e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
248d42cab1cSschwarze p->trailspace = 0;
249eeb5fd14Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
2506b4eabafSschwarze p->flags |= TERMP_NOSPACE;
2516b4eabafSschwarze }
252526e306bSschwarze return 0;
253e35cb253Sschwarze }
254e35cb253Sschwarze
255e35cb253Sschwarze static int
pre_PD(DECL_ARGS)256ffeff720Sschwarze pre_PD(DECL_ARGS)
257ffeff720Sschwarze {
258bcec2b4cSschwarze struct roffsu su;
259ffeff720Sschwarze
260ffeff720Sschwarze n = n->child;
261bcec2b4cSschwarze if (n == NULL) {
262ffeff720Sschwarze mt->pardist = 1;
263526e306bSschwarze return 0;
264ffeff720Sschwarze }
265d1982c71Sschwarze assert(n->type == ROFFT_TEXT);
266ecd22486Sschwarze if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
267bcec2b4cSschwarze mt->pardist = term_vspan(p, &su);
268526e306bSschwarze return 0;
269ffeff720Sschwarze }
270ffeff720Sschwarze
271ffeff720Sschwarze static int
pre_alternate(DECL_ARGS)272a2709d4eSschwarze pre_alternate(DECL_ARGS)
273f73abda9Skristaps {
274a2709d4eSschwarze enum termfont font[2];
2753a0d07afSschwarze struct roff_node *nn;
27672236cb3Sschwarze int i;
277f73abda9Skristaps
278a2709d4eSschwarze switch (n->tok) {
27949aff9f8Sschwarze case MAN_RB:
280a2709d4eSschwarze font[0] = TERMFONT_NONE;
281a2709d4eSschwarze font[1] = TERMFONT_BOLD;
282a2709d4eSschwarze break;
28349aff9f8Sschwarze case MAN_RI:
284a2709d4eSschwarze font[0] = TERMFONT_NONE;
285a2709d4eSschwarze font[1] = TERMFONT_UNDER;
286a2709d4eSschwarze break;
28749aff9f8Sschwarze case MAN_BR:
288a2709d4eSschwarze font[0] = TERMFONT_BOLD;
289a2709d4eSschwarze font[1] = TERMFONT_NONE;
290a2709d4eSschwarze break;
29149aff9f8Sschwarze case MAN_BI:
292a2709d4eSschwarze font[0] = TERMFONT_BOLD;
293a2709d4eSschwarze font[1] = TERMFONT_UNDER;
294a2709d4eSschwarze break;
29549aff9f8Sschwarze case MAN_IR:
296a2709d4eSschwarze font[0] = TERMFONT_UNDER;
297a2709d4eSschwarze font[1] = TERMFONT_NONE;
298a2709d4eSschwarze break;
29949aff9f8Sschwarze case MAN_IB:
300a2709d4eSschwarze font[0] = TERMFONT_UNDER;
301a2709d4eSschwarze font[1] = TERMFONT_BOLD;
302a2709d4eSschwarze break;
303a2709d4eSschwarze default:
304a2709d4eSschwarze abort();
305a2709d4eSschwarze }
30672236cb3Sschwarze for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i = 1 - i) {
307a2709d4eSschwarze term_fontrepl(p, font[i]);
30854421b68Sschwarze assert(nn->type == ROFFT_TEXT);
30954421b68Sschwarze term_word(p, nn->string);
310c4b0939cSschwarze if (nn->flags & NODE_EOS)
31154421b68Sschwarze p->flags |= TERMP_SENTENCE;
31293d668c7Sschwarze if (nn->next != NULL)
313f73abda9Skristaps p->flags |= TERMP_NOSPACE;
314f73abda9Skristaps }
315526e306bSschwarze return 0;
316f73abda9Skristaps }
317f73abda9Skristaps
318f73abda9Skristaps static int
pre_B(DECL_ARGS)319f73abda9Skristaps pre_B(DECL_ARGS)
320f73abda9Skristaps {
321fa70b73eSschwarze term_fontrepl(p, TERMFONT_BOLD);
322526e306bSschwarze return 1;
323f73abda9Skristaps }
324f73abda9Skristaps
325f73abda9Skristaps static int
pre_MR(DECL_ARGS)326f6697133Sschwarze pre_MR(DECL_ARGS)
327f6697133Sschwarze {
328f6697133Sschwarze term_fontrepl(p, TERMFONT_NONE);
329f6697133Sschwarze n = n->child;
330f6697133Sschwarze if (n != NULL) {
331f6697133Sschwarze term_word(p, n->string); /* name */
332f6697133Sschwarze p->flags |= TERMP_NOSPACE;
333f6697133Sschwarze }
334f6697133Sschwarze term_word(p, "(");
335f6697133Sschwarze p->flags |= TERMP_NOSPACE;
336f6697133Sschwarze if (n != NULL && (n = n->next) != NULL) {
337f6697133Sschwarze term_word(p, n->string); /* section */
338f6697133Sschwarze p->flags |= TERMP_NOSPACE;
339f6697133Sschwarze }
340f6697133Sschwarze term_word(p, ")");
341f6697133Sschwarze if (n != NULL && (n = n->next) != NULL) {
342f6697133Sschwarze p->flags |= TERMP_NOSPACE;
343f6697133Sschwarze term_word(p, n->string); /* suffix */
344f6697133Sschwarze }
345f6697133Sschwarze return 0;
346f6697133Sschwarze }
347f6697133Sschwarze
348f6697133Sschwarze static int
pre_OP(DECL_ARGS)34966ae7cc0Sschwarze pre_OP(DECL_ARGS)
35066ae7cc0Sschwarze {
35166ae7cc0Sschwarze term_word(p, "[");
352dfff190dSschwarze p->flags |= TERMP_KEEP | TERMP_NOSPACE;
35366ae7cc0Sschwarze
35493d668c7Sschwarze if ((n = n->child) != NULL) {
35566ae7cc0Sschwarze term_fontrepl(p, TERMFONT_BOLD);
35666ae7cc0Sschwarze term_word(p, n->string);
35766ae7cc0Sschwarze }
35893d668c7Sschwarze if (n != NULL && n->next != NULL) {
35966ae7cc0Sschwarze term_fontrepl(p, TERMFONT_UNDER);
36066ae7cc0Sschwarze term_word(p, n->next->string);
36166ae7cc0Sschwarze }
36266ae7cc0Sschwarze term_fontrepl(p, TERMFONT_NONE);
363dfff190dSschwarze p->flags &= ~TERMP_KEEP;
36466ae7cc0Sschwarze p->flags |= TERMP_NOSPACE;
36566ae7cc0Sschwarze term_word(p, "]");
366526e306bSschwarze return 0;
36766ae7cc0Sschwarze }
36866ae7cc0Sschwarze
36966ae7cc0Sschwarze static int
pre_in(DECL_ARGS)370ddce0b0cSschwarze pre_in(DECL_ARGS)
371e35cb253Sschwarze {
372bf8e53c9Sschwarze struct roffsu su;
373ddce0b0cSschwarze const char *cp;
374bf8e53c9Sschwarze size_t v;
375bf8e53c9Sschwarze int less;
376e35cb253Sschwarze
377e35cb253Sschwarze term_newln(p);
378ddce0b0cSschwarze
379e93ea447Sschwarze if (n->child == NULL) {
380e93ea447Sschwarze p->tcol->offset = mt->offset;
381526e306bSschwarze return 0;
382ddce0b0cSschwarze }
383ddce0b0cSschwarze
384ddce0b0cSschwarze cp = n->child->string;
385ddce0b0cSschwarze less = 0;
386ddce0b0cSschwarze
38793d668c7Sschwarze if (*cp == '-')
388ddce0b0cSschwarze less = -1;
38993d668c7Sschwarze else if (*cp == '+')
390ddce0b0cSschwarze less = 1;
391ddce0b0cSschwarze else
392ddce0b0cSschwarze cp--;
393ddce0b0cSschwarze
394ecd22486Sschwarze if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
395526e306bSschwarze return 0;
396ddce0b0cSschwarze
397f4692b45Sschwarze v = term_hen(p, &su);
398ddce0b0cSschwarze
399ddce0b0cSschwarze if (less < 0)
400e93ea447Sschwarze p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
401ddce0b0cSschwarze else if (less > 0)
402e93ea447Sschwarze p->tcol->offset += v;
403ddce0b0cSschwarze else
404e93ea447Sschwarze p->tcol->offset = v;
405e93ea447Sschwarze if (p->tcol->offset > SHRT_MAX)
406e93ea447Sschwarze p->tcol->offset = term_len(p, p->defindent);
407e35cb253Sschwarze
408526e306bSschwarze return 0;
409e35cb253Sschwarze }
410e35cb253Sschwarze
411e35cb253Sschwarze static int
pre_DT(DECL_ARGS)4128ea764d3Sschwarze pre_DT(DECL_ARGS)
4138ea764d3Sschwarze {
4148ea764d3Sschwarze term_tab_set(p, NULL);
4158ea764d3Sschwarze term_tab_set(p, "T");
4168ea764d3Sschwarze term_tab_set(p, ".5i");
4178ea764d3Sschwarze return 0;
4188ea764d3Sschwarze }
4198ea764d3Sschwarze
4208ea764d3Sschwarze static int
pre_HP(DECL_ARGS)421e35cb253Sschwarze pre_HP(DECL_ARGS)
422e35cb253Sschwarze {
423bf8e53c9Sschwarze struct roffsu su;
4243a0d07afSschwarze const struct roff_node *nn;
425bf8e53c9Sschwarze int len;
426e35cb253Sschwarze
427e35cb253Sschwarze switch (n->type) {
428d1982c71Sschwarze case ROFFT_BLOCK:
429ffeff720Sschwarze print_bvspace(p, n, mt->pardist);
430526e306bSschwarze return 1;
43193d668c7Sschwarze case ROFFT_HEAD:
43293d668c7Sschwarze return 0;
433d1982c71Sschwarze case ROFFT_BODY:
434e35cb253Sschwarze break;
435e35cb253Sschwarze default:
43693d668c7Sschwarze abort();
437e35cb253Sschwarze }
438e35cb253Sschwarze
43994a3c318Sschwarze if (n->child == NULL)
44094a3c318Sschwarze return 0;
44194a3c318Sschwarze
44294a3c318Sschwarze if ((n->child->flags & NODE_NOFILL) == 0) {
443eeb5fd14Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND;
444d42cab1cSschwarze p->trailspace = 2;
44508765786Sschwarze }
44608765786Sschwarze
447e35cb253Sschwarze /* Calculate offset. */
448e35cb253Sschwarze
449bf8e53c9Sschwarze if ((nn = n->parent->head->child) != NULL &&
450ecd22486Sschwarze a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
451f4692b45Sschwarze len = term_hen(p, &su);
4529327b421Sschwarze if (len < 0 && (size_t)(-len) > mt->offset)
4539327b421Sschwarze len = -mt->offset;
4549327b421Sschwarze else if (len > SHRT_MAX)
4559327b421Sschwarze len = term_len(p, p->defindent);
456bf8e53c9Sschwarze mt->lmargin[mt->lmargincur] = len;
457bf8e53c9Sschwarze } else
458bf8e53c9Sschwarze len = mt->lmargin[mt->lmargincur];
459e35cb253Sschwarze
460e93ea447Sschwarze p->tcol->offset = mt->offset;
461e93ea447Sschwarze p->tcol->rmargin = mt->offset + len;
462526e306bSschwarze return 1;
463e35cb253Sschwarze }
464e35cb253Sschwarze
465e35cb253Sschwarze static void
post_HP(DECL_ARGS)466e35cb253Sschwarze post_HP(DECL_ARGS)
467e35cb253Sschwarze {
468e35cb253Sschwarze switch (n->type) {
46993d668c7Sschwarze case ROFFT_BLOCK:
47093d668c7Sschwarze case ROFFT_HEAD:
47193d668c7Sschwarze break;
472d1982c71Sschwarze case ROFFT_BODY:
4731eef6025Sschwarze term_newln(p);
4749431f994Sschwarze
4759431f994Sschwarze /*
4769431f994Sschwarze * Compatibility with a groff bug.
4779431f994Sschwarze * The .HP macro uses the undocumented .tag request
4789431f994Sschwarze * which causes a line break and cancels no-space
4799431f994Sschwarze * mode even if there isn't any output.
4809431f994Sschwarze */
4819431f994Sschwarze
4829431f994Sschwarze if (n->child == NULL)
4839431f994Sschwarze term_vspace(p);
4849431f994Sschwarze
485eeb5fd14Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
486d42cab1cSschwarze p->trailspace = 0;
487e93ea447Sschwarze p->tcol->offset = mt->offset;
488e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
489e35cb253Sschwarze break;
490e35cb253Sschwarze default:
49193d668c7Sschwarze abort();
492e35cb253Sschwarze }
493e35cb253Sschwarze }
494e35cb253Sschwarze
495e35cb253Sschwarze static int
pre_PP(DECL_ARGS)496f73abda9Skristaps pre_PP(DECL_ARGS)
497f73abda9Skristaps {
498e35cb253Sschwarze switch (n->type) {
499d1982c71Sschwarze case ROFFT_BLOCK:
500*84680f53Sschwarze mt->lmargin[mt->lmargincur] = term_len(p, 7);
501ffeff720Sschwarze print_bvspace(p, n, mt->pardist);
502e35cb253Sschwarze break;
50393d668c7Sschwarze case ROFFT_HEAD:
50493d668c7Sschwarze return 0;
50593d668c7Sschwarze case ROFFT_BODY:
506e93ea447Sschwarze p->tcol->offset = mt->offset;
507e35cb253Sschwarze break;
50893d668c7Sschwarze default:
50993d668c7Sschwarze abort();
510e35cb253Sschwarze }
51193d668c7Sschwarze return 1;
512f73abda9Skristaps }
513f73abda9Skristaps
514f73abda9Skristaps static int
pre_IP(DECL_ARGS)515f73abda9Skristaps pre_IP(DECL_ARGS)
516f73abda9Skristaps {
517bf8e53c9Sschwarze struct roffsu su;
5183a0d07afSschwarze const struct roff_node *nn;
51972236cb3Sschwarze int len;
520f73abda9Skristaps
521e35cb253Sschwarze switch (n->type) {
52293d668c7Sschwarze case ROFFT_BLOCK:
52393d668c7Sschwarze print_bvspace(p, n, mt->pardist);
52493d668c7Sschwarze return 1;
525d1982c71Sschwarze case ROFFT_HEAD:
526e35cb253Sschwarze p->flags |= TERMP_NOBREAK;
527d42cab1cSschwarze p->trailspace = 1;
528e35cb253Sschwarze break;
52993d668c7Sschwarze case ROFFT_BODY:
53036ec5596Sschwarze p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
53193d668c7Sschwarze break;
532e35cb253Sschwarze default:
53393d668c7Sschwarze abort();
534e35cb253Sschwarze }
5350deb334fSschwarze
5366bd6754cSschwarze /* Calculate the offset from the optional second argument. */
537bf8e53c9Sschwarze if ((nn = n->parent->head->child) != NULL &&
538bf8e53c9Sschwarze (nn = nn->next) != NULL &&
539ecd22486Sschwarze a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
540f4692b45Sschwarze len = term_hen(p, &su);
541bf8e53c9Sschwarze if (len < 0 && (size_t)(-len) > mt->offset)
542bf8e53c9Sschwarze len = -mt->offset;
5439327b421Sschwarze else if (len > SHRT_MAX)
5449327b421Sschwarze len = term_len(p, p->defindent);
5459327b421Sschwarze mt->lmargin[mt->lmargincur] = len;
546bf8e53c9Sschwarze } else
547bf8e53c9Sschwarze len = mt->lmargin[mt->lmargincur];
548e35cb253Sschwarze
549e35cb253Sschwarze switch (n->type) {
550d1982c71Sschwarze case ROFFT_HEAD:
551e93ea447Sschwarze p->tcol->offset = mt->offset;
552e93ea447Sschwarze p->tcol->rmargin = mt->offset + len;
5530ac7e6ecSschwarze if (n->child != NULL)
5547ead8a4eSschwarze print_man_node(p, mt, n->child, meta);
555526e306bSschwarze return 0;
556d1982c71Sschwarze case ROFFT_BODY:
557e93ea447Sschwarze p->tcol->offset = mt->offset + len;
558e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
559e35cb253Sschwarze break;
560e35cb253Sschwarze default:
56193d668c7Sschwarze abort();
562e35cb253Sschwarze }
563526e306bSschwarze return 1;
564e35cb253Sschwarze }
565e35cb253Sschwarze
566e35cb253Sschwarze static void
post_IP(DECL_ARGS)567e35cb253Sschwarze post_IP(DECL_ARGS)
568e35cb253Sschwarze {
569e35cb253Sschwarze switch (n->type) {
57093d668c7Sschwarze case ROFFT_BLOCK:
57193d668c7Sschwarze break;
572d1982c71Sschwarze case ROFFT_HEAD:
573e35cb253Sschwarze term_flushln(p);
574e35cb253Sschwarze p->flags &= ~TERMP_NOBREAK;
575d42cab1cSschwarze p->trailspace = 0;
576e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
577e35cb253Sschwarze break;
578d1982c71Sschwarze case ROFFT_BODY:
5796bd6754cSschwarze term_newln(p);
580e93ea447Sschwarze p->tcol->offset = mt->offset;
581e35cb253Sschwarze break;
582e35cb253Sschwarze default:
58393d668c7Sschwarze abort();
584e35cb253Sschwarze }
585f73abda9Skristaps }
586f73abda9Skristaps
587f73abda9Skristaps static int
pre_TP(DECL_ARGS)588f73abda9Skristaps pre_TP(DECL_ARGS)
589f73abda9Skristaps {
590bf8e53c9Sschwarze struct roffsu su;
5913a0d07afSschwarze struct roff_node *nn;
59272236cb3Sschwarze int len;
593f73abda9Skristaps
594e35cb253Sschwarze switch (n->type) {
59593d668c7Sschwarze case ROFFT_BLOCK:
59693d668c7Sschwarze if (n->tok == MAN_TP)
59793d668c7Sschwarze print_bvspace(p, n, mt->pardist);
59893d668c7Sschwarze return 1;
599d1982c71Sschwarze case ROFFT_HEAD:
600b8d4b727Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
601d42cab1cSschwarze p->trailspace = 1;
602e35cb253Sschwarze break;
603d1982c71Sschwarze case ROFFT_BODY:
60436ec5596Sschwarze p->flags |= TERMP_NOSPACE | TERMP_NONEWLINE;
605e35cb253Sschwarze break;
606e35cb253Sschwarze default:
60793d668c7Sschwarze abort();
608e35cb253Sschwarze }
609e35cb253Sschwarze
610e35cb253Sschwarze /* Calculate offset. */
611e35cb253Sschwarze
612bf8e53c9Sschwarze if ((nn = n->parent->head->child) != NULL &&
613c4b0939cSschwarze nn->string != NULL && ! (NODE_LINE & nn->flags) &&
614ecd22486Sschwarze a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
615f4692b45Sschwarze len = term_hen(p, &su);
616bf8e53c9Sschwarze if (len < 0 && (size_t)(-len) > mt->offset)
617bf8e53c9Sschwarze len = -mt->offset;
6189327b421Sschwarze else if (len > SHRT_MAX)
6199327b421Sschwarze len = term_len(p, p->defindent);
6209327b421Sschwarze mt->lmargin[mt->lmargincur] = len;
621bf8e53c9Sschwarze } else
622bf8e53c9Sschwarze len = mt->lmargin[mt->lmargincur];
623e35cb253Sschwarze
624e35cb253Sschwarze switch (n->type) {
625d1982c71Sschwarze case ROFFT_HEAD:
626e93ea447Sschwarze p->tcol->offset = mt->offset;
627e93ea447Sschwarze p->tcol->rmargin = mt->offset + len;
628f73abda9Skristaps
629e35cb253Sschwarze /* Don't print same-line elements. */
630074125cbSschwarze nn = n->child;
63193d668c7Sschwarze while (nn != NULL && (nn->flags & NODE_LINE) == 0)
632074125cbSschwarze nn = nn->next;
633074125cbSschwarze
63493d668c7Sschwarze while (nn != NULL) {
6357ead8a4eSschwarze print_man_node(p, mt, nn, meta);
636074125cbSschwarze nn = nn->next;
637074125cbSschwarze }
638526e306bSschwarze return 0;
639d1982c71Sschwarze case ROFFT_BODY:
640e93ea447Sschwarze p->tcol->offset = mt->offset + len;
641e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
642d42cab1cSschwarze p->trailspace = 0;
643b8d4b727Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
644e35cb253Sschwarze break;
645e35cb253Sschwarze default:
64693d668c7Sschwarze abort();
647e35cb253Sschwarze }
648526e306bSschwarze return 1;
649e35cb253Sschwarze }
650e35cb253Sschwarze
651e35cb253Sschwarze static void
post_TP(DECL_ARGS)652e35cb253Sschwarze post_TP(DECL_ARGS)
653e35cb253Sschwarze {
654e35cb253Sschwarze switch (n->type) {
65593d668c7Sschwarze case ROFFT_BLOCK:
65693d668c7Sschwarze break;
657d1982c71Sschwarze case ROFFT_HEAD:
658e35cb253Sschwarze term_flushln(p);
659e35cb253Sschwarze break;
660d1982c71Sschwarze case ROFFT_BODY:
6616bd6754cSschwarze term_newln(p);
662e93ea447Sschwarze p->tcol->offset = mt->offset;
663e35cb253Sschwarze break;
664e35cb253Sschwarze default:
66593d668c7Sschwarze abort();
666e35cb253Sschwarze }
667f73abda9Skristaps }
668f73abda9Skristaps
669f73abda9Skristaps static int
pre_SS(DECL_ARGS)670f73abda9Skristaps pre_SS(DECL_ARGS)
671f73abda9Skristaps {
672ffeff720Sschwarze int i;
673f73abda9Skristaps
674e35cb253Sschwarze switch (n->type) {
675d1982c71Sschwarze case ROFFT_BLOCK:
676*84680f53Sschwarze mt->lmargin[mt->lmargincur] = term_len(p, 7);
677e12fe158Sschwarze mt->offset = term_len(p, p->defindent);
6785b8aa33bSschwarze
6795b8aa33bSschwarze /*
6805b8aa33bSschwarze * No vertical space before the first subsection
6815b8aa33bSschwarze * and after an empty subsection.
6825b8aa33bSschwarze */
6835b8aa33bSschwarze
6847ebbefbeSschwarze if ((n = roff_node_prev(n)) == NULL ||
6857ebbefbeSschwarze (n->tok == MAN_SS && roff_node_child(n->body) == NULL))
686e35cb253Sschwarze break;
6875b8aa33bSschwarze
688ffeff720Sschwarze for (i = 0; i < mt->pardist; i++)
689f73abda9Skristaps term_vspace(p);
690e35cb253Sschwarze break;
691d1982c71Sschwarze case ROFFT_HEAD:
692fa70b73eSschwarze term_fontrepl(p, TERMFONT_BOLD);
693e93ea447Sschwarze p->tcol->offset = term_len(p, 3);
694e93ea447Sschwarze p->tcol->rmargin = mt->offset;
69501788712Sschwarze p->trailspace = mt->offset;
69601788712Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND;
697e35cb253Sschwarze break;
698d1982c71Sschwarze case ROFFT_BODY:
699e93ea447Sschwarze p->tcol->offset = mt->offset;
700e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
70101788712Sschwarze p->trailspace = 0;
70201788712Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
703e35cb253Sschwarze break;
704e35cb253Sschwarze default:
705e35cb253Sschwarze break;
706e35cb253Sschwarze }
707526e306bSschwarze return 1;
708f73abda9Skristaps }
709f73abda9Skristaps
710f73abda9Skristaps static int
pre_SH(DECL_ARGS)711f73abda9Skristaps pre_SH(DECL_ARGS)
712f73abda9Skristaps {
713ffeff720Sschwarze int i;
714f73abda9Skristaps
715e35cb253Sschwarze switch (n->type) {
716d1982c71Sschwarze case ROFFT_BLOCK:
717*84680f53Sschwarze mt->lmargin[mt->lmargincur] = term_len(p, 7);
718e12fe158Sschwarze mt->offset = term_len(p, p->defindent);
7195b8aa33bSschwarze
7205b8aa33bSschwarze /*
7215b8aa33bSschwarze * No vertical space before the first section
7225b8aa33bSschwarze * and after an empty section.
7235b8aa33bSschwarze */
7245b8aa33bSschwarze
7257ebbefbeSschwarze if ((n = roff_node_prev(n)) == NULL ||
7267ebbefbeSschwarze (n->tok == MAN_SH && roff_node_child(n->body) == NULL))
727e35cb253Sschwarze break;
7285b8aa33bSschwarze
729ffeff720Sschwarze for (i = 0; i < mt->pardist; i++)
730f73abda9Skristaps term_vspace(p);
731e35cb253Sschwarze break;
732d1982c71Sschwarze case ROFFT_HEAD:
733fa70b73eSschwarze term_fontrepl(p, TERMFONT_BOLD);
734e93ea447Sschwarze p->tcol->offset = 0;
735e93ea447Sschwarze p->tcol->rmargin = mt->offset;
73601788712Sschwarze p->trailspace = mt->offset;
73701788712Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND;
738e35cb253Sschwarze break;
739d1982c71Sschwarze case ROFFT_BODY:
740e93ea447Sschwarze p->tcol->offset = mt->offset;
741e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
74201788712Sschwarze p->trailspace = 0;
74301788712Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
744e35cb253Sschwarze break;
745e35cb253Sschwarze default:
74693d668c7Sschwarze abort();
747e35cb253Sschwarze }
748526e306bSschwarze return 1;
749f73abda9Skristaps }
750f73abda9Skristaps
751f73abda9Skristaps static void
post_SH(DECL_ARGS)752f73abda9Skristaps post_SH(DECL_ARGS)
753f73abda9Skristaps {
754e35cb253Sschwarze switch (n->type) {
75593d668c7Sschwarze case ROFFT_BLOCK:
756e35cb253Sschwarze break;
75793d668c7Sschwarze case ROFFT_HEAD:
758d1982c71Sschwarze case ROFFT_BODY:
759e35cb253Sschwarze term_newln(p);
760e35cb253Sschwarze break;
761e35cb253Sschwarze default:
76293d668c7Sschwarze abort();
763e35cb253Sschwarze }
764f73abda9Skristaps }
765f73abda9Skristaps
766fbaebbd2Sschwarze static int
pre_RS(DECL_ARGS)767fbaebbd2Sschwarze pre_RS(DECL_ARGS)
768fbaebbd2Sschwarze {
769bf8e53c9Sschwarze struct roffsu su;
770fbaebbd2Sschwarze
771fbaebbd2Sschwarze switch (n->type) {
772d1982c71Sschwarze case ROFFT_BLOCK:
773fbaebbd2Sschwarze term_newln(p);
774526e306bSschwarze return 1;
775d1982c71Sschwarze case ROFFT_HEAD:
776526e306bSschwarze return 0;
77793d668c7Sschwarze case ROFFT_BODY:
778fbaebbd2Sschwarze break;
77993d668c7Sschwarze default:
78093d668c7Sschwarze abort();
781fbaebbd2Sschwarze }
782fbaebbd2Sschwarze
7830e724740Sschwarze n = n->parent->head;
7840e724740Sschwarze n->aux = SHRT_MAX + 1;
78562db3966Sschwarze if (n->child == NULL)
78662db3966Sschwarze n->aux = mt->lmargin[mt->lmargincur];
787ecd22486Sschwarze else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
788f4692b45Sschwarze n->aux = term_hen(p, &su);
7890e724740Sschwarze if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
7900e724740Sschwarze n->aux = -mt->offset;
7910e724740Sschwarze else if (n->aux > SHRT_MAX)
7920e724740Sschwarze n->aux = term_len(p, p->defindent);
793fbaebbd2Sschwarze
7940e724740Sschwarze mt->offset += n->aux;
795e93ea447Sschwarze p->tcol->offset = mt->offset;
796e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
797fbaebbd2Sschwarze
7986330f331Sschwarze if (++mt->lmarginsz < MAXMARGINS)
7996330f331Sschwarze mt->lmargincur = mt->lmarginsz;
8006330f331Sschwarze
801*84680f53Sschwarze mt->lmargin[mt->lmargincur] = term_len(p, 7);
802526e306bSschwarze return 1;
803fbaebbd2Sschwarze }
804fbaebbd2Sschwarze
805fbaebbd2Sschwarze static void
post_RS(DECL_ARGS)806fbaebbd2Sschwarze post_RS(DECL_ARGS)
807fbaebbd2Sschwarze {
808fbaebbd2Sschwarze switch (n->type) {
809d1982c71Sschwarze case ROFFT_BLOCK:
810d1982c71Sschwarze case ROFFT_HEAD:
811296801eeSschwarze return;
81293d668c7Sschwarze case ROFFT_BODY:
813fbaebbd2Sschwarze break;
81493d668c7Sschwarze default:
81593d668c7Sschwarze abort();
816fbaebbd2Sschwarze }
81793d668c7Sschwarze term_newln(p);
8180e724740Sschwarze mt->offset -= n->parent->head->aux;
819e93ea447Sschwarze p->tcol->offset = mt->offset;
8206330f331Sschwarze if (--mt->lmarginsz < MAXMARGINS)
8216330f331Sschwarze mt->lmargincur = mt->lmarginsz;
822296801eeSschwarze }
823fbaebbd2Sschwarze
8243aeff926Sschwarze static int
pre_SY(DECL_ARGS)8255e5a9c61Sschwarze pre_SY(DECL_ARGS)
8265e5a9c61Sschwarze {
8275e5a9c61Sschwarze const struct roff_node *nn;
8285e5a9c61Sschwarze int len;
8295e5a9c61Sschwarze
8305e5a9c61Sschwarze switch (n->type) {
8315e5a9c61Sschwarze case ROFFT_BLOCK:
8327ebbefbeSschwarze if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY)
8335e5a9c61Sschwarze print_bvspace(p, n, mt->pardist);
8345e5a9c61Sschwarze return 1;
8355e5a9c61Sschwarze case ROFFT_HEAD:
8365e5a9c61Sschwarze case ROFFT_BODY:
8375e5a9c61Sschwarze break;
8385e5a9c61Sschwarze default:
8395e5a9c61Sschwarze abort();
8405e5a9c61Sschwarze }
8415e5a9c61Sschwarze
8425e5a9c61Sschwarze nn = n->parent->head->child;
8434bc55941Sschwarze len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
8445e5a9c61Sschwarze
8455e5a9c61Sschwarze switch (n->type) {
8465e5a9c61Sschwarze case ROFFT_HEAD:
8475e5a9c61Sschwarze p->tcol->offset = mt->offset;
8485e5a9c61Sschwarze p->tcol->rmargin = mt->offset + len;
8491e7e4b62Sschwarze if (n->next->child == NULL ||
8501e7e4b62Sschwarze (n->next->child->flags & NODE_NOFILL) == 0)
8515e5a9c61Sschwarze p->flags |= TERMP_NOBREAK;
8525e5a9c61Sschwarze term_fontrepl(p, TERMFONT_BOLD);
8535e5a9c61Sschwarze break;
8545e5a9c61Sschwarze case ROFFT_BODY:
8555e5a9c61Sschwarze mt->lmargin[mt->lmargincur] = len;
8565e5a9c61Sschwarze p->tcol->offset = mt->offset + len;
8575e5a9c61Sschwarze p->tcol->rmargin = p->maxrmargin;
8585e5a9c61Sschwarze p->flags |= TERMP_NOSPACE;
8595e5a9c61Sschwarze break;
8605e5a9c61Sschwarze default:
8615e5a9c61Sschwarze abort();
8625e5a9c61Sschwarze }
8635e5a9c61Sschwarze return 1;
8645e5a9c61Sschwarze }
8655e5a9c61Sschwarze
8665e5a9c61Sschwarze static void
post_SY(DECL_ARGS)8675e5a9c61Sschwarze post_SY(DECL_ARGS)
8685e5a9c61Sschwarze {
8695e5a9c61Sschwarze switch (n->type) {
87093d668c7Sschwarze case ROFFT_BLOCK:
87193d668c7Sschwarze break;
8725e5a9c61Sschwarze case ROFFT_HEAD:
8735e5a9c61Sschwarze term_flushln(p);
8745e5a9c61Sschwarze p->flags &= ~TERMP_NOBREAK;
8755e5a9c61Sschwarze break;
8765e5a9c61Sschwarze case ROFFT_BODY:
8775e5a9c61Sschwarze term_newln(p);
8785e5a9c61Sschwarze p->tcol->offset = mt->offset;
8795e5a9c61Sschwarze break;
8805e5a9c61Sschwarze default:
88193d668c7Sschwarze abort();
8825e5a9c61Sschwarze }
8835e5a9c61Sschwarze }
8845e5a9c61Sschwarze
8855e5a9c61Sschwarze static int
pre_UR(DECL_ARGS)8863aeff926Sschwarze pre_UR(DECL_ARGS)
8873aeff926Sschwarze {
888526e306bSschwarze return n->type != ROFFT_HEAD;
8893aeff926Sschwarze }
8903aeff926Sschwarze
8913aeff926Sschwarze static void
post_UR(DECL_ARGS)8923aeff926Sschwarze post_UR(DECL_ARGS)
8933aeff926Sschwarze {
894d1982c71Sschwarze if (n->type != ROFFT_BLOCK)
8953aeff926Sschwarze return;
8963aeff926Sschwarze
8973aeff926Sschwarze term_word(p, "<");
8983aeff926Sschwarze p->flags |= TERMP_NOSPACE;
8993aeff926Sschwarze
90093d668c7Sschwarze if (n->child->child != NULL)
9013aeff926Sschwarze print_man_node(p, mt, n->child->child, meta);
9023aeff926Sschwarze
9033aeff926Sschwarze p->flags |= TERMP_NOSPACE;
9043aeff926Sschwarze term_word(p, ">");
9053aeff926Sschwarze }
9063aeff926Sschwarze
907f73abda9Skristaps static void
print_man_node(DECL_ARGS)908b822ca0dSschwarze print_man_node(DECL_ARGS)
909f73abda9Skristaps {
91016fe0cfcSschwarze const struct man_term_act *act;
911fa70b73eSschwarze int c;
912f73abda9Skristaps
91336ec5596Sschwarze /*
91436ec5596Sschwarze * In no-fill mode, break the output line at the beginning
91536ec5596Sschwarze * of new input lines except after \c, and nowhere else.
91636ec5596Sschwarze */
91736ec5596Sschwarze
91836ec5596Sschwarze if (n->flags & NODE_NOFILL) {
91936ec5596Sschwarze if (n->flags & NODE_LINE &&
92036ec5596Sschwarze (p->flags & TERMP_NONEWLINE) == 0)
92136ec5596Sschwarze term_newln(p);
92236ec5596Sschwarze p->flags |= TERMP_BRNEVER;
92318bbf166Sschwarze } else {
92418bbf166Sschwarze if (n->flags & NODE_LINE)
92518bbf166Sschwarze term_tab_ref(p);
92636ec5596Sschwarze p->flags &= ~TERMP_BRNEVER;
92718bbf166Sschwarze }
92836ec5596Sschwarze
9290ac7e6ecSschwarze if (n->flags & NODE_ID)
9300ac7e6ecSschwarze term_tag_write(n, p->line);
9310ac7e6ecSschwarze
932f73abda9Skristaps switch (n->type) {
933d1982c71Sschwarze case ROFFT_TEXT:
93439fa4f70Sschwarze /*
93539fa4f70Sschwarze * If we have a blank line, output a vertical space.
93639fa4f70Sschwarze * If we have a space as the first character, break
93739fa4f70Sschwarze * before printing the line's data.
93839fa4f70Sschwarze */
93974591f1eSschwarze if (*n->string == '\0') {
9403361af39Sschwarze if (p->flags & TERMP_NONEWLINE)
9413361af39Sschwarze term_newln(p);
9423361af39Sschwarze else
943f73abda9Skristaps term_vspace(p);
94439fa4f70Sschwarze return;
94574591f1eSschwarze } else if (*n->string == ' ' && n->flags & NODE_LINE &&
94674591f1eSschwarze (p->flags & TERMP_NONEWLINE) == 0)
947aad3ea0cSschwarze term_newln(p);
948252b7e4aSschwarze else if (n->flags & NODE_DELIMC)
949252b7e4aSschwarze p->flags |= TERMP_NOSPACE;
950aad3ea0cSschwarze
951f73abda9Skristaps term_word(p, n->string);
95208765786Sschwarze goto out;
9534c293873Sschwarze case ROFFT_COMMENT:
9544c293873Sschwarze return;
955d1982c71Sschwarze case ROFFT_EQN:
956c4b0939cSschwarze if ( ! (n->flags & NODE_LINE))
95796e1823cSschwarze p->flags |= TERMP_NOSPACE;
958f8618d99Sschwarze term_eqn(p, n->eqn);
959c4b0939cSschwarze if (n->next != NULL && ! (n->next->flags & NODE_LINE))
960e2624e2fSschwarze p->flags |= TERMP_NOSPACE;
9618d973ab1Sschwarze return;
962d1982c71Sschwarze case ROFFT_TBL:
963945de12cSschwarze if (p->tbl.cols == NULL)
964004600a2Sschwarze term_newln(p);
9652791bd1cSschwarze term_tbl(p, n->span);
96639fa4f70Sschwarze return;
967f73abda9Skristaps default:
968f73abda9Skristaps break;
969f73abda9Skristaps }
970f73abda9Skristaps
97129478532Sschwarze if (n->tok < ROFF_MAX) {
97296a5de47Sschwarze roff_term_pre(p, n);
97329478532Sschwarze return;
97429478532Sschwarze }
97529478532Sschwarze
97616fe0cfcSschwarze act = man_term_act(n->tok);
97763f82d49Sschwarze if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
97839fa4f70Sschwarze term_fontrepl(p, TERMFONT_NONE);
97939fa4f70Sschwarze
98039fa4f70Sschwarze c = 1;
98116fe0cfcSschwarze if (act->pre != NULL)
98216fe0cfcSschwarze c = (*act->pre)(p, mt, n, meta);
98339fa4f70Sschwarze
98493d668c7Sschwarze if (c && n->child != NULL)
9857ead8a4eSschwarze print_man_nodelist(p, mt, n->child, meta);
986f73abda9Skristaps
98716fe0cfcSschwarze if (act->post != NULL)
98816fe0cfcSschwarze (*act->post)(p, mt, n, meta);
98963f82d49Sschwarze if ((act->flags & MAN_NOTEXT) == 0 && n->tok != MAN_SM)
990fa70b73eSschwarze term_fontrepl(p, TERMFONT_NONE);
991bc49dbe1Sschwarze
99208765786Sschwarze out:
99336ec5596Sschwarze if (n->parent->tok == MAN_HP && n->parent->type == ROFFT_BODY &&
99436ec5596Sschwarze n->prev == NULL && n->flags & NODE_NOFILL) {
99508765786Sschwarze term_newln(p);
996e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
997e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
99824f1eaadSschwarze }
99993d668c7Sschwarze if (n->flags & NODE_EOS)
1000bc49dbe1Sschwarze p->flags |= TERMP_SENTENCE;
1001f73abda9Skristaps }
1002f73abda9Skristaps
1003f73abda9Skristaps static void
print_man_nodelist(DECL_ARGS)1004fa70b73eSschwarze print_man_nodelist(DECL_ARGS)
1005f73abda9Skristaps {
1006e4534905Sschwarze while (n != NULL) {
10077ead8a4eSschwarze print_man_node(p, mt, n, meta);
1008e4534905Sschwarze n = n->next;
1009e4534905Sschwarze }
1010f73abda9Skristaps }
1011f73abda9Skristaps
1012f73abda9Skristaps static void
print_man_foot(struct termp * p,const struct roff_meta * meta)10132a238f45Sschwarze print_man_foot(struct termp *p, const struct roff_meta *meta)
1014f73abda9Skristaps {
1015a92c1cd8Sschwarze char *title;
101601e00683Sschwarze size_t datelen, titlen;
1017f95d589eSschwarze
1018649d1604Sschwarze assert(meta->title);
1019649d1604Sschwarze assert(meta->msec);
1020649d1604Sschwarze assert(meta->date);
1021f73abda9Skristaps
1022fa70b73eSschwarze term_fontrepl(p, TERMFONT_NONE);
1023fa70b73eSschwarze
102443edbcc8Sschwarze if (meta->hasbody)
1025f73abda9Skristaps term_vspace(p);
102684dd50f0Sschwarze
102784dd50f0Sschwarze /*
102884dd50f0Sschwarze * Temporary, undocumented option to imitate mdoc(7) output.
10292a238f45Sschwarze * In the bottom right corner, use the operating system
10302a238f45Sschwarze * instead of the title.
103184dd50f0Sschwarze */
103284dd50f0Sschwarze
1033304a35f5Sschwarze if ( ! p->mdocstyle) {
1034a92c1cd8Sschwarze mandoc_asprintf(&title, "%s(%s)",
1035a92c1cd8Sschwarze meta->title, meta->msec);
103693d668c7Sschwarze } else if (meta->os != NULL) {
10372a238f45Sschwarze title = mandoc_strdup(meta->os);
1038304a35f5Sschwarze } else {
1039a92c1cd8Sschwarze title = mandoc_strdup("");
1040304a35f5Sschwarze }
1041c9c50c67Sschwarze datelen = term_strlen(p, meta->date);
1042f73abda9Skristaps
10432a238f45Sschwarze /* Bottom left corner: operating system. */
104484dd50f0Sschwarze
1045f73abda9Skristaps p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1046d42cab1cSschwarze p->trailspace = 1;
1047e93ea447Sschwarze p->tcol->offset = 0;
1048e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin > datelen ?
104901e00683Sschwarze (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
10507586f75aSschwarze
10512a238f45Sschwarze if (meta->os)
10522a238f45Sschwarze term_word(p, meta->os);
1053f73abda9Skristaps term_flushln(p);
1054f73abda9Skristaps
105584dd50f0Sschwarze /* At the bottom in the middle: manual date. */
105684dd50f0Sschwarze
1057e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
105801e00683Sschwarze titlen = term_strlen(p, title);
1059e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin > titlen ?
1060e93ea447Sschwarze p->maxrmargin - titlen : 0;
106101e00683Sschwarze p->flags |= TERMP_NOSPACE;
1062f73abda9Skristaps
1063b058e777Sschwarze term_word(p, meta->date);
1064f73abda9Skristaps term_flushln(p);
1065c9c50c67Sschwarze
106684dd50f0Sschwarze /* Bottom right corner: manual title and section. */
106784dd50f0Sschwarze
1068c9c50c67Sschwarze p->flags &= ~TERMP_NOBREAK;
1069c9c50c67Sschwarze p->flags |= TERMP_NOSPACE;
1070d42cab1cSschwarze p->trailspace = 0;
1071e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
1072e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
1073c9c50c67Sschwarze
1074c9c50c67Sschwarze term_word(p, title);
1075c9c50c67Sschwarze term_flushln(p);
10764d699dd2Sschwarze
10774d699dd2Sschwarze /*
10784d699dd2Sschwarze * Reset the terminal state for more output after the footer:
10794d699dd2Sschwarze * Some output modes, in particular PostScript and PDF, print
10804d699dd2Sschwarze * the header and the footer into a buffer such that it can be
10814d699dd2Sschwarze * reused for multiple output pages, then go on to format the
10824d699dd2Sschwarze * main text.
10834d699dd2Sschwarze */
10844d699dd2Sschwarze
10854d699dd2Sschwarze p->tcol->offset = 0;
10864d699dd2Sschwarze p->flags = 0;
10874d699dd2Sschwarze
1088a92c1cd8Sschwarze free(title);
1089f73abda9Skristaps }
1090f73abda9Skristaps
1091f73abda9Skristaps static void
print_man_head(struct termp * p,const struct roff_meta * meta)10922a238f45Sschwarze print_man_head(struct termp *p, const struct roff_meta *meta)
1093f73abda9Skristaps {
10940b2f1307Sschwarze const char *volume;
1095a92c1cd8Sschwarze char *title;
10960b2f1307Sschwarze size_t vollen, titlen;
1097f95d589eSschwarze
10987ead8a4eSschwarze assert(meta->title);
10997ead8a4eSschwarze assert(meta->msec);
1100f73abda9Skristaps
11010b2f1307Sschwarze volume = NULL == meta->vol ? "" : meta->vol;
11020b2f1307Sschwarze vollen = term_strlen(p, volume);
1103f73abda9Skristaps
110484dd50f0Sschwarze /* Top left corner: manual title and section. */
110584dd50f0Sschwarze
1106a92c1cd8Sschwarze mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
11073ebeb861Sschwarze titlen = term_strlen(p, title);
1108f73abda9Skristaps
110904bba6bcSschwarze p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1110d42cab1cSschwarze p->trailspace = 1;
1111e93ea447Sschwarze p->tcol->offset = 0;
1112e93ea447Sschwarze p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
11130b2f1307Sschwarze (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
111401e00683Sschwarze vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1115f73abda9Skristaps
1116f73abda9Skristaps term_word(p, title);
1117f73abda9Skristaps term_flushln(p);
1118f73abda9Skristaps
111984dd50f0Sschwarze /* At the top in the middle: manual volume. */
112084dd50f0Sschwarze
11216b4eabafSschwarze p->flags |= TERMP_NOSPACE;
1122e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
1123e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1124e93ea447Sschwarze p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1125f73abda9Skristaps
11260b2f1307Sschwarze term_word(p, volume);
1127f73abda9Skristaps term_flushln(p);
1128f73abda9Skristaps
112984dd50f0Sschwarze /* Top right corner: title and section, again. */
113084dd50f0Sschwarze
1131a23405b5Sschwarze p->flags &= ~TERMP_NOBREAK;
1132d42cab1cSschwarze p->trailspace = 0;
1133e93ea447Sschwarze if (p->tcol->rmargin + titlen <= p->maxrmargin) {
11346b4eabafSschwarze p->flags |= TERMP_NOSPACE;
1135e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
1136e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
1137f73abda9Skristaps term_word(p, title);
1138f73abda9Skristaps term_flushln(p);
1139a23405b5Sschwarze }
1140f73abda9Skristaps
1141f73abda9Skristaps p->flags &= ~TERMP_NOSPACE;
1142e93ea447Sschwarze p->tcol->offset = 0;
1143e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
1144fccfce9dSschwarze
1145fccfce9dSschwarze /*
114684dd50f0Sschwarze * Groff prints three blank lines before the content.
114784dd50f0Sschwarze * Do the same, except in the temporary, undocumented
114884dd50f0Sschwarze * mode imitating mdoc(7) output.
1149fccfce9dSschwarze */
1150fccfce9dSschwarze
1151fccfce9dSschwarze term_vspace(p);
1152a92c1cd8Sschwarze free(title);
1153304a35f5Sschwarze }
1154