1/*	$Id: roff_term.c,v 1.19 2019/01/04 03:24:33 schwarze Exp $ */
2/*
3 * Copyright (c) 2010,2014,2015,2017-2019 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 <sys/types.h>
18
19#include <assert.h>
20#include <stdio.h>
21#include <string.h>
22
23#include "mandoc.h"
24#include "roff.h"
25#include "out.h"
26#include "term.h"
27
28#define	ROFF_TERM_ARGS struct termp *p, const struct roff_node *n
29
30typedef	void	(*roff_term_pre_fp)(ROFF_TERM_ARGS);
31
32static	void	  roff_term_pre_br(ROFF_TERM_ARGS);
33static	void	  roff_term_pre_ce(ROFF_TERM_ARGS);
34static	void	  roff_term_pre_ft(ROFF_TERM_ARGS);
35static	void	  roff_term_pre_ll(ROFF_TERM_ARGS);
36static	void	  roff_term_pre_mc(ROFF_TERM_ARGS);
37static	void	  roff_term_pre_po(ROFF_TERM_ARGS);
38static	void	  roff_term_pre_sp(ROFF_TERM_ARGS);
39static	void	  roff_term_pre_ta(ROFF_TERM_ARGS);
40static	void	  roff_term_pre_ti(ROFF_TERM_ARGS);
41
42static	const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = {
43	roff_term_pre_br,  /* br */
44	roff_term_pre_ce,  /* ce */
45	roff_term_pre_br,  /* fi */
46	roff_term_pre_ft,  /* ft */
47	roff_term_pre_ll,  /* ll */
48	roff_term_pre_mc,  /* mc */
49	roff_term_pre_br,  /* nf */
50	roff_term_pre_po,  /* po */
51	roff_term_pre_ce,  /* rj */
52	roff_term_pre_sp,  /* sp */
53	roff_term_pre_ta,  /* ta */
54	roff_term_pre_ti,  /* ti */
55};
56
57
58void
59roff_term_pre(struct termp *p, const struct roff_node *n)
60{
61	assert(n->tok < ROFF_MAX);
62	(*roff_term_pre_acts[n->tok])(p, n);
63}
64
65static void
66roff_term_pre_br(ROFF_TERM_ARGS)
67{
68	term_newln(p);
69	if (p->flags & TERMP_BRIND) {
70		p->tcol->offset = p->tcol->rmargin;
71		p->tcol->rmargin = p->maxrmargin;
72		p->trailspace = 0;
73		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
74		p->flags |= TERMP_NOSPACE;
75	}
76}
77
78static void
79roff_term_pre_ce(ROFF_TERM_ARGS)
80{
81	const struct roff_node	*nc1, *nc2;
82
83	roff_term_pre_br(p, n);
84	p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT;
85	nc1 = n->child->next;
86	while (nc1 != NULL) {
87		nc2 = nc1;
88		do {
89			nc2 = nc2->next;
90		} while (nc2 != NULL && (nc2->type != ROFFT_TEXT ||
91		    (nc2->flags & NODE_LINE) == 0));
92		while (nc1 != nc2) {
93			if (nc1->type == ROFFT_TEXT)
94				term_word(p, nc1->string);
95			else
96				roff_term_pre(p, nc1);
97			nc1 = nc1->next;
98		}
99		p->flags |= TERMP_NOSPACE;
100		term_flushln(p);
101	}
102	p->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
103}
104
105static void
106roff_term_pre_ft(ROFF_TERM_ARGS)
107{
108	const char	*cp;
109
110	cp = n->child->string;
111	switch (mandoc_font(cp, (int)strlen(cp))) {
112	case ESCAPE_FONTBOLD:
113		term_fontrepl(p, TERMFONT_BOLD);
114		break;
115	case ESCAPE_FONTITALIC:
116		term_fontrepl(p, TERMFONT_UNDER);
117		break;
118	case ESCAPE_FONTBI:
119		term_fontrepl(p, TERMFONT_BI);
120		break;
121	case ESCAPE_FONTPREV:
122		term_fontlast(p);
123		break;
124	case ESCAPE_FONTROMAN:
125	case ESCAPE_FONTCW:
126		term_fontrepl(p, TERMFONT_NONE);
127		break;
128	default:
129		break;
130	}
131}
132
133static void
134roff_term_pre_ll(ROFF_TERM_ARGS)
135{
136	term_setwidth(p, n->child != NULL ? n->child->string : NULL);
137}
138
139static void
140roff_term_pre_mc(ROFF_TERM_ARGS)
141{
142	if (p->col) {
143		p->flags |= TERMP_NOBREAK;
144		term_flushln(p);
145		p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE);
146	}
147	if (n->child != NULL) {
148		p->mc = n->child->string;
149		p->flags |= TERMP_NEWMC;
150	} else
151		p->flags |= TERMP_ENDMC;
152}
153
154static void
155roff_term_pre_po(ROFF_TERM_ARGS)
156{
157	struct roffsu	 su;
158	static int	 po, polast;
159	int		 ponew;
160
161	if (n->child != NULL &&
162	    a2roffsu(n->child->string, &su, SCALE_EM) != NULL) {
163		ponew = term_hen(p, &su);
164		if (*n->child->string == '+' ||
165		    *n->child->string == '-')
166			ponew += po;
167	} else
168		ponew = polast;
169	polast = po;
170	po = ponew;
171
172	ponew = po - polast + (int)p->tcol->offset;
173	p->tcol->offset = ponew > 0 ? ponew : 0;
174}
175
176static void
177roff_term_pre_sp(ROFF_TERM_ARGS)
178{
179	struct roffsu	 su;
180	int		 len;
181
182	if (n->child != NULL) {
183		if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL)
184			su.scale = 1.0;
185		len = term_vspan(p, &su);
186	} else
187		len = 1;
188
189	if (len < 0)
190		p->skipvsp -= len;
191	else
192		while (len--)
193			term_vspace(p);
194
195	roff_term_pre_br(p, n);
196}
197
198static void
199roff_term_pre_ta(ROFF_TERM_ARGS)
200{
201	term_tab_set(p, NULL);
202	for (n = n->child; n != NULL; n = n->next)
203		term_tab_set(p, n->string);
204}
205
206static void
207roff_term_pre_ti(ROFF_TERM_ARGS)
208{
209	struct roffsu	 su;
210	const char	*cp;
211	int		 len, sign;
212
213	roff_term_pre_br(p, n);
214
215	if (n->child == NULL)
216		return;
217	cp = n->child->string;
218	if (*cp == '+') {
219		sign = 1;
220		cp++;
221	} else if (*cp == '-') {
222		sign = -1;
223		cp++;
224	} else
225		sign = 0;
226
227	if (a2roffsu(cp, &su, SCALE_EM) == NULL)
228		return;
229	len = term_hen(p, &su);
230
231	if (sign == 0) {
232		p->ti = len - p->tcol->offset;
233		p->tcol->offset = len;
234	} else if (sign == 1) {
235		p->ti = len;
236		p->tcol->offset += len;
237	} else if ((size_t)len < p->tcol->offset) {
238		p->ti = -len;
239		p->tcol->offset -= len;
240	} else {
241		p->ti = -p->tcol->offset;
242		p->tcol->offset = 0;
243	}
244}
245