xref: /freebsd/contrib/mandoc/roff_term.c (revision d5b0e70f7e04d971691517ce1304d86a1e367e2e)
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 
32 typedef	void	(*roff_term_pre_fp)(ROFF_TERM_ARGS);
33 
34 static	void	  roff_term_pre_br(ROFF_TERM_ARGS);
35 static	void	  roff_term_pre_ce(ROFF_TERM_ARGS);
36 static	void	  roff_term_pre_ft(ROFF_TERM_ARGS);
37 static	void	  roff_term_pre_ll(ROFF_TERM_ARGS);
38 static	void	  roff_term_pre_mc(ROFF_TERM_ARGS);
39 static	void	  roff_term_pre_po(ROFF_TERM_ARGS);
40 static	void	  roff_term_pre_sp(ROFF_TERM_ARGS);
41 static	void	  roff_term_pre_ta(ROFF_TERM_ARGS);
42 static	void	  roff_term_pre_ti(ROFF_TERM_ARGS);
43 
44 static	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 
60 void
61 roff_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 
67 static void
68 roff_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 
80 static void
81 roff_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 
107 static void
108 roff_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 
137 static void
138 roff_term_pre_ll(ROFF_TERM_ARGS)
139 {
140 	term_setwidth(p, n->child != NULL ? n->child->string : NULL);
141 }
142 
143 static void
144 roff_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 
158 static void
159 roff_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 
188 static void
189 roff_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 
210 static void
211 roff_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 
218 static void
219 roff_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