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