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