1/* $OpenBSD: roff_term.c,v 1.20 2020/09/03 17:37:06 schwarze Exp $ */
2/*
3 * Copyright (c) 2010,2014,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include "config.h"
18
19#include <sys/types.h>
20
21#include <assert.h>
22#include <stdio.h>
23#include <string.h>
24
25#include "mandoc.h"
26#include "roff.h"
27#include "out.h"
28#include "term.h"
29
30#define	ROFF_TERM_ARGS struct termp *p, const struct roff_node *n
31
32typedef	void	(*roff_term_pre_fp)(ROFF_TERM_ARGS);
33
34static	void	  roff_term_pre_br(ROFF_TERM_ARGS);
35static	void	  roff_term_pre_ce(ROFF_TERM_ARGS);
36static	void	  roff_term_pre_ft(ROFF_TERM_ARGS);
37static	void	  roff_term_pre_ll(ROFF_TERM_ARGS);
38static	void	  roff_term_pre_mc(ROFF_TERM_ARGS);
39static	void	  roff_term_pre_po(ROFF_TERM_ARGS);
40static	void	  roff_term_pre_sp(ROFF_TERM_ARGS);
41static	void	  roff_term_pre_ta(ROFF_TERM_ARGS);
42static	void	  roff_term_pre_ti(ROFF_TERM_ARGS);
43
44static	const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = {
45	roff_term_pre_br,  /* br */
46	roff_term_pre_ce,  /* ce */
47	roff_term_pre_br,  /* fi */
48	roff_term_pre_ft,  /* ft */
49	roff_term_pre_ll,  /* ll */
50	roff_term_pre_mc,  /* mc */
51	roff_term_pre_br,  /* nf */
52	roff_term_pre_po,  /* po */
53	roff_term_pre_ce,  /* rj */
54	roff_term_pre_sp,  /* sp */
55	roff_term_pre_ta,  /* ta */
56	roff_term_pre_ti,  /* ti */
57};
58
59
60void
61roff_term_pre(struct termp *p, const struct roff_node *n)
62{
63	assert(n->tok < ROFF_MAX);
64	(*roff_term_pre_acts[n->tok])(p, n);
65}
66
67static void
68roff_term_pre_br(ROFF_TERM_ARGS)
69{
70	term_newln(p);
71	if (p->flags & TERMP_BRIND) {
72		p->tcol->offset = p->tcol->rmargin;
73		p->tcol->rmargin = p->maxrmargin;
74		p->trailspace = 0;
75		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
76		p->flags |= TERMP_NOSPACE;
77	}
78}
79
80static void
81roff_term_pre_ce(ROFF_TERM_ARGS)
82{
83	const struct roff_node	*nc1, *nc2;
84
85	roff_term_pre_br(p, n);
86	p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT;
87	nc1 = n->child->next;
88	while (nc1 != NULL) {
89		nc2 = nc1;
90		do {
91			nc2 = nc2->next;
92		} while (nc2 != NULL && (nc2->type != ROFFT_TEXT ||
93		    (nc2->flags & NODE_LINE) == 0));
94		while (nc1 != nc2) {
95			if (nc1->type == ROFFT_TEXT)
96				term_word(p, nc1->string);
97			else
98				roff_term_pre(p, nc1);
99			nc1 = nc1->next;
100		}
101		p->flags |= TERMP_NOSPACE;
102		term_flushln(p);
103	}
104	p->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
105}
106
107static void
108roff_term_pre_ft(ROFF_TERM_ARGS)
109{
110	const char	*cp;
111
112	cp = n->child->string;
113	switch (mandoc_font(cp, (int)strlen(cp))) {
114	case ESCAPE_FONTBOLD:
115	case ESCAPE_FONTCB:
116		term_fontrepl(p, TERMFONT_BOLD);
117		break;
118	case ESCAPE_FONTITALIC:
119	case ESCAPE_FONTCI:
120		term_fontrepl(p, TERMFONT_UNDER);
121		break;
122	case ESCAPE_FONTBI:
123		term_fontrepl(p, TERMFONT_BI);
124		break;
125	case ESCAPE_FONTPREV:
126		term_fontlast(p);
127		break;
128	case ESCAPE_FONTROMAN:
129	case ESCAPE_FONTCR:
130		term_fontrepl(p, TERMFONT_NONE);
131		break;
132	default:
133		break;
134	}
135}
136
137static void
138roff_term_pre_ll(ROFF_TERM_ARGS)
139{
140	term_setwidth(p, n->child != NULL ? n->child->string : NULL);
141}
142
143static void
144roff_term_pre_mc(ROFF_TERM_ARGS)
145{
146	if (p->col) {
147		p->flags |= TERMP_NOBREAK;
148		term_flushln(p);
149		p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE);
150	}
151	if (n->child != NULL) {
152		p->mc = n->child->string;
153		p->flags |= TERMP_NEWMC;
154	} else
155		p->flags |= TERMP_ENDMC;
156}
157
158static void
159roff_term_pre_po(ROFF_TERM_ARGS)
160{
161	struct roffsu	 su;
162	static int	 po, pouse, polast;
163	int		 ponew;
164
165	/* Revert the currently active page offset. */
166	p->tcol->offset -= pouse;
167
168	/* Determine the requested page offset. */
169	if (n->child != NULL &&
170	    a2roffsu(n->child->string, &su, SCALE_EM) != NULL) {
171		ponew = term_hen(p, &su);
172		if (*n->child->string == '+' ||
173		    *n->child->string == '-')
174			ponew += po;
175	} else
176		ponew = polast;
177
178	/* Remeber both the previous and the newly requested offset. */
179	polast = po;
180	po = ponew;
181
182	/* Truncate to the range [-offset, 60], remember, and apply it. */
183	pouse = po >= 60 ? 60 :
184	    po < -(int)p->tcol->offset ? -(int)p->tcol->offset : po;
185	p->tcol->offset += pouse;
186}
187
188static void
189roff_term_pre_sp(ROFF_TERM_ARGS)
190{
191	struct roffsu	 su;
192	int		 len;
193
194	if (n->child != NULL) {
195		if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL)
196			su.scale = 1.0;
197		len = term_vspan(p, &su);
198	} else
199		len = 1;
200
201	if (len < 0)
202		p->skipvsp -= len;
203	else
204		while (len--)
205			term_vspace(p);
206
207	roff_term_pre_br(p, n);
208}
209
210static void
211roff_term_pre_ta(ROFF_TERM_ARGS)
212{
213	term_tab_set(p, NULL);
214	for (n = n->child; n != NULL; n = n->next)
215		term_tab_set(p, n->string);
216}
217
218static void
219roff_term_pre_ti(ROFF_TERM_ARGS)
220{
221	struct roffsu	 su;
222	const char	*cp;
223	const size_t	 maxoff = 72;
224	int		 len, sign;
225
226	roff_term_pre_br(p, n);
227
228	if (n->child == NULL)
229		return;
230	cp = n->child->string;
231	if (*cp == '+') {
232		sign = 1;
233		cp++;
234	} else if (*cp == '-') {
235		sign = -1;
236		cp++;
237	} else
238		sign = 0;
239
240	if (a2roffsu(cp, &su, SCALE_EM) == NULL)
241		return;
242	len = term_hen(p, &su);
243
244	switch (sign) {
245	case 1:
246		if (p->tcol->offset + len <= maxoff)
247			p->ti = len;
248		else if (p->tcol->offset < maxoff)
249			p->ti = maxoff - p->tcol->offset;
250		else
251			p->ti = 0;
252		break;
253	case -1:
254		if ((size_t)len < p->tcol->offset)
255			p->ti = -len;
256		else
257			p->ti = -p->tcol->offset;
258		break;
259	default:
260		if ((size_t)len > maxoff)
261			len = maxoff;
262		p->ti = len - p->tcol->offset;
263		break;
264	}
265	p->tcol->offset += p->ti;
266}
267