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