1 |
|
|
/* $OpenBSD: term.c,v 1.117 2016/03/20 16:50:30 krw Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
4 |
|
|
* Copyright (c) 2010-2015 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 AUTHORS DISCLAIM ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <sys/types.h> |
19 |
|
|
|
20 |
|
|
#include <assert.h> |
21 |
|
|
#include <ctype.h> |
22 |
|
|
#include <stdio.h> |
23 |
|
|
#include <stdlib.h> |
24 |
|
|
#include <string.h> |
25 |
|
|
|
26 |
|
|
#include "mandoc.h" |
27 |
|
|
#include "mandoc_aux.h" |
28 |
|
|
#include "out.h" |
29 |
|
|
#include "term.h" |
30 |
|
|
#include "main.h" |
31 |
|
|
|
32 |
|
|
static size_t cond_width(const struct termp *, int, int *); |
33 |
|
|
static void adjbuf(struct termp *p, size_t); |
34 |
|
|
static void bufferc(struct termp *, char); |
35 |
|
|
static void encode(struct termp *, const char *, size_t); |
36 |
|
|
static void encode1(struct termp *, int); |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
void |
40 |
|
|
term_free(struct termp *p) |
41 |
|
|
{ |
42 |
|
|
|
43 |
|
|
free(p->buf); |
44 |
|
|
free(p->fontq); |
45 |
|
|
free(p); |
46 |
|
|
} |
47 |
|
|
|
48 |
|
|
void |
49 |
|
|
term_begin(struct termp *p, term_margin head, |
50 |
|
|
term_margin foot, const struct roff_meta *arg) |
51 |
|
|
{ |
52 |
|
|
|
53 |
|
|
p->headf = head; |
54 |
|
|
p->footf = foot; |
55 |
|
|
p->argf = arg; |
56 |
|
|
(*p->begin)(p); |
57 |
|
|
} |
58 |
|
|
|
59 |
|
|
void |
60 |
|
|
term_end(struct termp *p) |
61 |
|
|
{ |
62 |
|
|
|
63 |
|
|
(*p->end)(p); |
64 |
|
|
} |
65 |
|
|
|
66 |
|
|
/* |
67 |
|
|
* Flush a chunk of text. By default, break the output line each time |
68 |
|
|
* the right margin is reached, and continue output on the next line |
69 |
|
|
* at the same offset as the chunk itself. By default, also break the |
70 |
|
|
* output line at the end of the chunk. |
71 |
|
|
* The following flags may be specified: |
72 |
|
|
* |
73 |
|
|
* - TERMP_NOBREAK: Do not break the output line at the right margin, |
74 |
|
|
* but only at the max right margin. Also, do not break the output |
75 |
|
|
* line at the end of the chunk, such that the next call can pad to |
76 |
|
|
* the next column. However, if less than p->trailspace blanks, |
77 |
|
|
* which can be 0, 1, or 2, remain to the right margin, the line |
78 |
|
|
* will be broken. |
79 |
|
|
* - TERMP_BRTRSP: Consider trailing whitespace significant |
80 |
|
|
* when deciding whether the chunk fits or not. |
81 |
|
|
* - TERMP_BRIND: If the chunk does not fit and the output line has |
82 |
|
|
* to be broken, start the next line at the right margin instead |
83 |
|
|
* of at the offset. Used together with TERMP_NOBREAK for the tags |
84 |
|
|
* in various kinds of tagged lists. |
85 |
|
|
* - TERMP_DANGLE: Do not break the output line at the right margin, |
86 |
|
|
* append the next chunk after it even if this one is too long. |
87 |
|
|
* To be used together with TERMP_NOBREAK. |
88 |
|
|
* - TERMP_HANG: Like TERMP_DANGLE, and also suppress padding before |
89 |
|
|
* the next chunk if this column is not full. |
90 |
|
|
*/ |
91 |
|
|
void |
92 |
|
|
term_flushln(struct termp *p) |
93 |
|
|
{ |
94 |
|
|
size_t i; /* current input position in p->buf */ |
95 |
|
|
int ntab; /* number of tabs to prepend */ |
96 |
|
|
size_t vis; /* current visual position on output */ |
97 |
|
|
size_t vbl; /* number of blanks to prepend to output */ |
98 |
|
|
size_t vend; /* end of word visual position on output */ |
99 |
|
|
size_t bp; /* visual right border position */ |
100 |
|
|
size_t dv; /* temporary for visual pos calculations */ |
101 |
|
|
size_t j; /* temporary loop index for p->buf */ |
102 |
|
|
size_t jhy; /* last hyph before overflow w/r/t j */ |
103 |
|
|
size_t maxvis; /* output position of visible boundary */ |
104 |
|
|
|
105 |
|
|
/* |
106 |
|
|
* First, establish the maximum columns of "visible" content. |
107 |
|
|
* This is usually the difference between the right-margin and |
108 |
|
|
* an indentation, but can be, for tagged lists or columns, a |
109 |
|
|
* small set of values. |
110 |
|
|
* |
111 |
|
|
* The following unsigned-signed subtractions look strange, |
112 |
|
|
* but they are actually correct. If the int p->overstep |
113 |
|
|
* is negative, it gets sign extended. Subtracting that |
114 |
|
|
* very large size_t effectively adds a small number to dv. |
115 |
|
|
*/ |
116 |
|
|
dv = p->rmargin > p->offset ? p->rmargin - p->offset : 0; |
117 |
|
|
maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; |
118 |
|
|
|
119 |
|
|
if (p->flags & TERMP_NOBREAK) { |
120 |
|
|
dv = p->maxrmargin > p->offset ? |
121 |
|
|
p->maxrmargin - p->offset : 0; |
122 |
|
|
bp = (int)dv > p->overstep ? |
123 |
|
|
dv - (size_t)p->overstep : 0; |
124 |
|
|
} else |
125 |
|
|
bp = maxvis; |
126 |
|
|
|
127 |
|
|
/* |
128 |
|
|
* Calculate the required amount of padding. |
129 |
|
|
*/ |
130 |
|
|
vbl = p->offset + p->overstep > p->viscol ? |
131 |
|
|
p->offset + p->overstep - p->viscol : 0; |
132 |
|
|
|
133 |
|
|
vis = vend = 0; |
134 |
|
|
i = 0; |
135 |
|
|
|
136 |
|
|
while (i < p->col) { |
137 |
|
|
/* |
138 |
|
|
* Handle literal tab characters: collapse all |
139 |
|
|
* subsequent tabs into a single huge set of spaces. |
140 |
|
|
*/ |
141 |
|
|
ntab = 0; |
142 |
|
|
while (i < p->col && '\t' == p->buf[i]) { |
143 |
|
|
vend = (vis / p->tabwidth + 1) * p->tabwidth; |
144 |
|
|
vbl += vend - vis; |
145 |
|
|
vis = vend; |
146 |
|
|
ntab++; |
147 |
|
|
i++; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
/* |
151 |
|
|
* Count up visible word characters. Control sequences |
152 |
|
|
* (starting with the CSI) aren't counted. A space |
153 |
|
|
* generates a non-printing word, which is valid (the |
154 |
|
|
* space is printed according to regular spacing rules). |
155 |
|
|
*/ |
156 |
|
|
|
157 |
|
|
for (j = i, jhy = 0; j < p->col; j++) { |
158 |
|
|
if (' ' == p->buf[j] || '\t' == p->buf[j]) |
159 |
|
|
break; |
160 |
|
|
|
161 |
|
|
/* Back over the last printed character. */ |
162 |
|
|
if (8 == p->buf[j]) { |
163 |
|
|
assert(j); |
164 |
|
|
vend -= (*p->width)(p, p->buf[j - 1]); |
165 |
|
|
continue; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
/* Regular word. */ |
169 |
|
|
/* Break at the hyphen point if we overrun. */ |
170 |
|
|
if (vend > vis && vend < bp && |
171 |
|
|
(ASCII_HYPH == p->buf[j] || |
172 |
|
|
ASCII_BREAK == p->buf[j])) |
173 |
|
|
jhy = j; |
174 |
|
|
|
175 |
|
|
/* |
176 |
|
|
* Hyphenation now decided, put back a real |
177 |
|
|
* hyphen such that we get the correct width. |
178 |
|
|
*/ |
179 |
|
|
if (ASCII_HYPH == p->buf[j]) |
180 |
|
|
p->buf[j] = '-'; |
181 |
|
|
|
182 |
|
|
vend += (*p->width)(p, p->buf[j]); |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
/* |
186 |
|
|
* Find out whether we would exceed the right margin. |
187 |
|
|
* If so, break to the next line. |
188 |
|
|
*/ |
189 |
|
|
if (vend > bp && 0 == jhy && vis > 0) { |
190 |
|
|
vend -= vis; |
191 |
|
|
(*p->endline)(p); |
192 |
|
|
p->viscol = 0; |
193 |
|
|
if (TERMP_BRIND & p->flags) { |
194 |
|
|
vbl = p->rmargin; |
195 |
|
|
vend += p->rmargin; |
196 |
|
|
vend -= p->offset; |
197 |
|
|
} else |
198 |
|
|
vbl = p->offset; |
199 |
|
|
|
200 |
|
|
/* use pending tabs on the new line */ |
201 |
|
|
|
202 |
|
|
if (0 < ntab) |
203 |
|
|
vbl += ntab * p->tabwidth; |
204 |
|
|
|
205 |
|
|
/* |
206 |
|
|
* Remove the p->overstep width. |
207 |
|
|
* Again, if p->overstep is negative, |
208 |
|
|
* sign extension does the right thing. |
209 |
|
|
*/ |
210 |
|
|
|
211 |
|
|
bp += (size_t)p->overstep; |
212 |
|
|
p->overstep = 0; |
213 |
|
|
} |
214 |
|
|
|
215 |
|
|
/* Write out the [remaining] word. */ |
216 |
|
|
for ( ; i < p->col; i++) { |
217 |
|
|
if (vend > bp && jhy > 0 && i > jhy) |
218 |
|
|
break; |
219 |
|
|
if ('\t' == p->buf[i]) |
220 |
|
|
break; |
221 |
|
|
if (' ' == p->buf[i]) { |
222 |
|
|
j = i; |
223 |
|
|
while (i < p->col && ' ' == p->buf[i]) |
224 |
|
|
i++; |
225 |
|
|
dv = (i - j) * (*p->width)(p, ' '); |
226 |
|
|
vbl += dv; |
227 |
|
|
vend += dv; |
228 |
|
|
break; |
229 |
|
|
} |
230 |
|
|
if (ASCII_NBRSP == p->buf[i]) { |
231 |
|
|
vbl += (*p->width)(p, ' '); |
232 |
|
|
continue; |
233 |
|
|
} |
234 |
|
|
if (ASCII_BREAK == p->buf[i]) |
235 |
|
|
continue; |
236 |
|
|
|
237 |
|
|
/* |
238 |
|
|
* Now we definitely know there will be |
239 |
|
|
* printable characters to output, |
240 |
|
|
* so write preceding white space now. |
241 |
|
|
*/ |
242 |
|
|
if (vbl) { |
243 |
|
|
(*p->advance)(p, vbl); |
244 |
|
|
p->viscol += vbl; |
245 |
|
|
vbl = 0; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
(*p->letter)(p, p->buf[i]); |
249 |
|
|
if (8 == p->buf[i]) |
250 |
|
|
p->viscol -= (*p->width)(p, p->buf[i-1]); |
251 |
|
|
else |
252 |
|
|
p->viscol += (*p->width)(p, p->buf[i]); |
253 |
|
|
} |
254 |
|
|
vis = vend; |
255 |
|
|
} |
256 |
|
|
|
257 |
|
|
/* |
258 |
|
|
* If there was trailing white space, it was not printed; |
259 |
|
|
* so reset the cursor position accordingly. |
260 |
|
|
*/ |
261 |
|
|
if (vis > vbl) |
262 |
|
|
vis -= vbl; |
263 |
|
|
else |
264 |
|
|
vis = 0; |
265 |
|
|
|
266 |
|
|
p->col = 0; |
267 |
|
|
p->overstep = 0; |
268 |
|
|
p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE); |
269 |
|
|
|
270 |
|
|
if ( ! (TERMP_NOBREAK & p->flags)) { |
271 |
|
|
p->viscol = 0; |
272 |
|
|
(*p->endline)(p); |
273 |
|
|
return; |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
if (TERMP_HANG & p->flags) { |
277 |
|
|
p->overstep += (int)(p->offset + vis - p->rmargin + |
278 |
|
|
p->trailspace * (*p->width)(p, ' ')); |
279 |
|
|
|
280 |
|
|
/* |
281 |
|
|
* If we have overstepped the margin, temporarily move |
282 |
|
|
* it to the right and flag the rest of the line to be |
283 |
|
|
* shorter. |
284 |
|
|
* If there is a request to keep the columns together, |
285 |
|
|
* allow negative overstep when the column is not full. |
286 |
|
|
*/ |
287 |
|
|
if (p->trailspace && p->overstep < 0) |
288 |
|
|
p->overstep = 0; |
289 |
|
|
return; |
290 |
|
|
|
291 |
|
|
} else if (TERMP_DANGLE & p->flags) |
292 |
|
|
return; |
293 |
|
|
|
294 |
|
|
/* Trailing whitespace is significant in some columns. */ |
295 |
|
|
if (vis && vbl && (TERMP_BRTRSP & p->flags)) |
296 |
|
|
vis += vbl; |
297 |
|
|
|
298 |
|
|
/* If the column was overrun, break the line. */ |
299 |
|
|
if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { |
300 |
|
|
(*p->endline)(p); |
301 |
|
|
p->viscol = 0; |
302 |
|
|
} |
303 |
|
|
} |
304 |
|
|
|
305 |
|
|
/* |
306 |
|
|
* A newline only breaks an existing line; it won't assert vertical |
307 |
|
|
* space. All data in the output buffer is flushed prior to the newline |
308 |
|
|
* assertion. |
309 |
|
|
*/ |
310 |
|
|
void |
311 |
|
|
term_newln(struct termp *p) |
312 |
|
|
{ |
313 |
|
|
|
314 |
|
|
p->flags |= TERMP_NOSPACE; |
315 |
|
|
if (p->col || p->viscol) |
316 |
|
|
term_flushln(p); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
/* |
320 |
|
|
* Asserts a vertical space (a full, empty line-break between lines). |
321 |
|
|
* Note that if used twice, this will cause two blank spaces and so on. |
322 |
|
|
* All data in the output buffer is flushed prior to the newline |
323 |
|
|
* assertion. |
324 |
|
|
*/ |
325 |
|
|
void |
326 |
|
|
term_vspace(struct termp *p) |
327 |
|
|
{ |
328 |
|
|
|
329 |
|
|
term_newln(p); |
330 |
|
|
p->viscol = 0; |
331 |
|
|
if (0 < p->skipvsp) |
332 |
|
|
p->skipvsp--; |
333 |
|
|
else |
334 |
|
|
(*p->endline)(p); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
/* Swap current and previous font; for \fP and .ft P */ |
338 |
|
|
void |
339 |
|
|
term_fontlast(struct termp *p) |
340 |
|
|
{ |
341 |
|
|
enum termfont f; |
342 |
|
|
|
343 |
|
|
f = p->fontl; |
344 |
|
|
p->fontl = p->fontq[p->fonti]; |
345 |
|
|
p->fontq[p->fonti] = f; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
/* Set font, save current, discard previous; for \f, .ft, .B etc. */ |
349 |
|
|
void |
350 |
|
|
term_fontrepl(struct termp *p, enum termfont f) |
351 |
|
|
{ |
352 |
|
|
|
353 |
|
|
p->fontl = p->fontq[p->fonti]; |
354 |
|
|
p->fontq[p->fonti] = f; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
/* Set font, save previous. */ |
358 |
|
|
void |
359 |
|
|
term_fontpush(struct termp *p, enum termfont f) |
360 |
|
|
{ |
361 |
|
|
|
362 |
|
|
p->fontl = p->fontq[p->fonti]; |
363 |
|
|
if (++p->fonti == p->fontsz) { |
364 |
|
|
p->fontsz += 8; |
365 |
|
|
p->fontq = mandoc_reallocarray(p->fontq, |
366 |
|
|
p->fontsz, sizeof(*p->fontq)); |
367 |
|
|
} |
368 |
|
|
p->fontq[p->fonti] = f; |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
/* Flush to make the saved pointer current again. */ |
372 |
|
|
void |
373 |
|
|
term_fontpopq(struct termp *p, int i) |
374 |
|
|
{ |
375 |
|
|
|
376 |
|
|
assert(i >= 0); |
377 |
|
|
if (p->fonti > i) |
378 |
|
|
p->fonti = i; |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
/* Pop one font off the stack. */ |
382 |
|
|
void |
383 |
|
|
term_fontpop(struct termp *p) |
384 |
|
|
{ |
385 |
|
|
|
386 |
|
|
assert(p->fonti); |
387 |
|
|
p->fonti--; |
388 |
|
|
} |
389 |
|
|
|
390 |
|
|
/* |
391 |
|
|
* Handle pwords, partial words, which may be either a single word or a |
392 |
|
|
* phrase that cannot be broken down (such as a literal string). This |
393 |
|
|
* handles word styling. |
394 |
|
|
*/ |
395 |
|
|
void |
396 |
|
|
term_word(struct termp *p, const char *word) |
397 |
|
|
{ |
398 |
|
|
const char nbrsp[2] = { ASCII_NBRSP, 0 }; |
399 |
|
|
const char *seq, *cp; |
400 |
|
|
int sz, uc; |
401 |
|
|
size_t ssz; |
402 |
|
|
enum mandoc_esc esc; |
403 |
|
|
|
404 |
|
|
if ( ! (TERMP_NOSPACE & p->flags)) { |
405 |
|
|
if ( ! (TERMP_KEEP & p->flags)) { |
406 |
|
|
bufferc(p, ' '); |
407 |
|
|
if (TERMP_SENTENCE & p->flags) |
408 |
|
|
bufferc(p, ' '); |
409 |
|
|
} else |
410 |
|
|
bufferc(p, ASCII_NBRSP); |
411 |
|
|
} |
412 |
|
|
if (TERMP_PREKEEP & p->flags) |
413 |
|
|
p->flags |= TERMP_KEEP; |
414 |
|
|
|
415 |
|
|
if ( ! (p->flags & TERMP_NONOSPACE)) |
416 |
|
|
p->flags &= ~TERMP_NOSPACE; |
417 |
|
|
else |
418 |
|
|
p->flags |= TERMP_NOSPACE; |
419 |
|
|
|
420 |
|
|
p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); |
421 |
|
|
p->skipvsp = 0; |
422 |
|
|
|
423 |
|
|
while ('\0' != *word) { |
424 |
|
|
if ('\\' != *word) { |
425 |
|
|
if (TERMP_NBRWORD & p->flags) { |
426 |
|
|
if (' ' == *word) { |
427 |
|
|
encode(p, nbrsp, 1); |
428 |
|
|
word++; |
429 |
|
|
continue; |
430 |
|
|
} |
431 |
|
|
ssz = strcspn(word, "\\ "); |
432 |
|
|
} else |
433 |
|
|
ssz = strcspn(word, "\\"); |
434 |
|
|
encode(p, word, ssz); |
435 |
|
|
word += (int)ssz; |
436 |
|
|
continue; |
437 |
|
|
} |
438 |
|
|
|
439 |
|
|
word++; |
440 |
|
|
esc = mandoc_escape(&word, &seq, &sz); |
441 |
|
|
if (ESCAPE_ERROR == esc) |
442 |
|
|
continue; |
443 |
|
|
|
444 |
|
|
switch (esc) { |
445 |
|
|
case ESCAPE_UNICODE: |
446 |
|
|
uc = mchars_num2uc(seq + 1, sz - 1); |
447 |
|
|
break; |
448 |
|
|
case ESCAPE_NUMBERED: |
449 |
|
|
uc = mchars_num2char(seq, sz); |
450 |
|
|
if (uc < 0) |
451 |
|
|
continue; |
452 |
|
|
break; |
453 |
|
|
case ESCAPE_SPECIAL: |
454 |
|
|
if (p->enc == TERMENC_ASCII) { |
455 |
|
|
cp = mchars_spec2str(seq, sz, &ssz); |
456 |
|
|
if (cp != NULL) |
457 |
|
|
encode(p, cp, ssz); |
458 |
|
|
} else { |
459 |
|
|
uc = mchars_spec2cp(seq, sz); |
460 |
|
|
if (uc > 0) |
461 |
|
|
encode1(p, uc); |
462 |
|
|
} |
463 |
|
|
continue; |
464 |
|
|
case ESCAPE_FONTBOLD: |
465 |
|
|
term_fontrepl(p, TERMFONT_BOLD); |
466 |
|
|
continue; |
467 |
|
|
case ESCAPE_FONTITALIC: |
468 |
|
|
term_fontrepl(p, TERMFONT_UNDER); |
469 |
|
|
continue; |
470 |
|
|
case ESCAPE_FONTBI: |
471 |
|
|
term_fontrepl(p, TERMFONT_BI); |
472 |
|
|
continue; |
473 |
|
|
case ESCAPE_FONT: |
474 |
|
|
case ESCAPE_FONTROMAN: |
475 |
|
|
term_fontrepl(p, TERMFONT_NONE); |
476 |
|
|
continue; |
477 |
|
|
case ESCAPE_FONTPREV: |
478 |
|
|
term_fontlast(p); |
479 |
|
|
continue; |
480 |
|
|
case ESCAPE_NOSPACE: |
481 |
|
|
if (p->flags & TERMP_BACKAFTER) |
482 |
|
|
p->flags &= ~TERMP_BACKAFTER; |
483 |
|
|
else if (*word == '\0') |
484 |
|
|
p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); |
485 |
|
|
continue; |
486 |
|
|
case ESCAPE_SKIPCHAR: |
487 |
|
|
p->flags |= TERMP_BACKAFTER; |
488 |
|
|
continue; |
489 |
|
|
case ESCAPE_OVERSTRIKE: |
490 |
|
|
cp = seq + sz; |
491 |
|
|
while (seq < cp) { |
492 |
|
|
if (*seq == '\\') { |
493 |
|
|
mandoc_escape(&seq, NULL, NULL); |
494 |
|
|
continue; |
495 |
|
|
} |
496 |
|
|
encode1(p, *seq++); |
497 |
|
|
if (seq < cp) { |
498 |
|
|
if (p->flags & TERMP_BACKBEFORE) |
499 |
|
|
p->flags |= TERMP_BACKAFTER; |
500 |
|
|
else |
501 |
|
|
p->flags |= TERMP_BACKBEFORE; |
502 |
|
|
} |
503 |
|
|
} |
504 |
|
|
/* Trim trailing backspace/blank pair. */ |
505 |
|
|
if (p->col > 2 && p->buf[p->col - 1] == ' ') |
506 |
|
|
p->col -= 2; |
507 |
|
|
continue; |
508 |
|
|
default: |
509 |
|
|
continue; |
510 |
|
|
} |
511 |
|
|
|
512 |
|
|
/* |
513 |
|
|
* Common handling for Unicode and numbered |
514 |
|
|
* character escape sequences. |
515 |
|
|
*/ |
516 |
|
|
|
517 |
|
|
if (p->enc == TERMENC_ASCII) { |
518 |
|
|
cp = ascii_uc2str(uc); |
519 |
|
|
encode(p, cp, strlen(cp)); |
520 |
|
|
} else { |
521 |
|
|
if ((uc < 0x20 && uc != 0x09) || |
522 |
|
|
(uc > 0x7E && uc < 0xA0)) |
523 |
|
|
uc = 0xFFFD; |
524 |
|
|
encode1(p, uc); |
525 |
|
|
} |
526 |
|
|
} |
527 |
|
|
p->flags &= ~TERMP_NBRWORD; |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
static void |
531 |
|
|
adjbuf(struct termp *p, size_t sz) |
532 |
|
|
{ |
533 |
|
|
|
534 |
|
|
if (0 == p->maxcols) |
535 |
|
|
p->maxcols = 1024; |
536 |
|
|
while (sz >= p->maxcols) |
537 |
|
|
p->maxcols <<= 2; |
538 |
|
|
|
539 |
|
|
p->buf = mandoc_reallocarray(p->buf, p->maxcols, sizeof(int)); |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
static void |
543 |
|
|
bufferc(struct termp *p, char c) |
544 |
|
|
{ |
545 |
|
|
|
546 |
|
|
if (p->col + 1 >= p->maxcols) |
547 |
|
|
adjbuf(p, p->col + 1); |
548 |
|
|
|
549 |
|
|
p->buf[p->col++] = c; |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
/* |
553 |
|
|
* See encode(). |
554 |
|
|
* Do this for a single (probably unicode) value. |
555 |
|
|
* Does not check for non-decorated glyphs. |
556 |
|
|
*/ |
557 |
|
|
static void |
558 |
|
|
encode1(struct termp *p, int c) |
559 |
|
|
{ |
560 |
|
|
enum termfont f; |
561 |
|
|
|
562 |
|
|
if (p->col + 7 >= p->maxcols) |
563 |
|
|
adjbuf(p, p->col + 7); |
564 |
|
|
|
565 |
|
|
f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ? |
566 |
|
|
p->fontq[p->fonti] : TERMFONT_NONE; |
567 |
|
|
|
568 |
|
|
if (p->flags & TERMP_BACKBEFORE) { |
569 |
|
|
if (p->buf[p->col - 1] == ' ') |
570 |
|
|
p->col--; |
571 |
|
|
else |
572 |
|
|
p->buf[p->col++] = 8; |
573 |
|
|
p->flags &= ~TERMP_BACKBEFORE; |
574 |
|
|
} |
575 |
|
|
if (TERMFONT_UNDER == f || TERMFONT_BI == f) { |
576 |
|
|
p->buf[p->col++] = '_'; |
577 |
|
|
p->buf[p->col++] = 8; |
578 |
|
|
} |
579 |
|
|
if (TERMFONT_BOLD == f || TERMFONT_BI == f) { |
580 |
|
|
if (ASCII_HYPH == c) |
581 |
|
|
p->buf[p->col++] = '-'; |
582 |
|
|
else |
583 |
|
|
p->buf[p->col++] = c; |
584 |
|
|
p->buf[p->col++] = 8; |
585 |
|
|
} |
586 |
|
|
p->buf[p->col++] = c; |
587 |
|
|
if (p->flags & TERMP_BACKAFTER) { |
588 |
|
|
p->flags |= TERMP_BACKBEFORE; |
589 |
|
|
p->flags &= ~TERMP_BACKAFTER; |
590 |
|
|
} |
591 |
|
|
} |
592 |
|
|
|
593 |
|
|
static void |
594 |
|
|
encode(struct termp *p, const char *word, size_t sz) |
595 |
|
|
{ |
596 |
|
|
size_t i; |
597 |
|
|
|
598 |
|
|
if (p->col + 2 + (sz * 5) >= p->maxcols) |
599 |
|
|
adjbuf(p, p->col + 2 + (sz * 5)); |
600 |
|
|
|
601 |
|
|
for (i = 0; i < sz; i++) { |
602 |
|
|
if (ASCII_HYPH == word[i] || |
603 |
|
|
isgraph((unsigned char)word[i])) |
604 |
|
|
encode1(p, word[i]); |
605 |
|
|
else |
606 |
|
|
p->buf[p->col++] = word[i]; |
607 |
|
|
} |
608 |
|
|
} |
609 |
|
|
|
610 |
|
|
void |
611 |
|
|
term_setwidth(struct termp *p, const char *wstr) |
612 |
|
|
{ |
613 |
|
|
struct roffsu su; |
614 |
|
|
int iop, width; |
615 |
|
|
|
616 |
|
|
iop = 0; |
617 |
|
|
width = 0; |
618 |
|
|
if (NULL != wstr) { |
619 |
|
|
switch (*wstr) { |
620 |
|
|
case '+': |
621 |
|
|
iop = 1; |
622 |
|
|
wstr++; |
623 |
|
|
break; |
624 |
|
|
case '-': |
625 |
|
|
iop = -1; |
626 |
|
|
wstr++; |
627 |
|
|
break; |
628 |
|
|
default: |
629 |
|
|
break; |
630 |
|
|
} |
631 |
|
|
if (a2roffsu(wstr, &su, SCALE_MAX)) |
632 |
|
|
width = term_hspan(p, &su); |
633 |
|
|
else |
634 |
|
|
iop = 0; |
635 |
|
|
} |
636 |
|
|
(*p->setwidth)(p, iop, width); |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
size_t |
640 |
|
|
term_len(const struct termp *p, size_t sz) |
641 |
|
|
{ |
642 |
|
|
|
643 |
|
|
return (*p->width)(p, ' ') * sz; |
644 |
|
|
} |
645 |
|
|
|
646 |
|
|
static size_t |
647 |
|
|
cond_width(const struct termp *p, int c, int *skip) |
648 |
|
|
{ |
649 |
|
|
|
650 |
|
|
if (*skip) { |
651 |
|
|
(*skip) = 0; |
652 |
|
|
return 0; |
653 |
|
|
} else |
654 |
|
|
return (*p->width)(p, c); |
655 |
|
|
} |
656 |
|
|
|
657 |
|
|
size_t |
658 |
|
|
term_strlen(const struct termp *p, const char *cp) |
659 |
|
|
{ |
660 |
|
|
size_t sz, rsz, i; |
661 |
|
|
int ssz, skip, uc; |
662 |
|
|
const char *seq, *rhs; |
663 |
|
|
enum mandoc_esc esc; |
664 |
|
|
static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH, |
665 |
|
|
ASCII_BREAK, '\0' }; |
666 |
|
|
|
667 |
|
|
/* |
668 |
|
|
* Account for escaped sequences within string length |
669 |
|
|
* calculations. This follows the logic in term_word() as we |
670 |
|
|
* must calculate the width of produced strings. |
671 |
|
|
*/ |
672 |
|
|
|
673 |
|
|
sz = 0; |
674 |
|
|
skip = 0; |
675 |
|
|
while ('\0' != *cp) { |
676 |
|
|
rsz = strcspn(cp, rej); |
677 |
|
|
for (i = 0; i < rsz; i++) |
678 |
|
|
sz += cond_width(p, *cp++, &skip); |
679 |
|
|
|
680 |
|
|
switch (*cp) { |
681 |
|
|
case '\\': |
682 |
|
|
cp++; |
683 |
|
|
esc = mandoc_escape(&cp, &seq, &ssz); |
684 |
|
|
if (ESCAPE_ERROR == esc) |
685 |
|
|
continue; |
686 |
|
|
|
687 |
|
|
rhs = NULL; |
688 |
|
|
|
689 |
|
|
switch (esc) { |
690 |
|
|
case ESCAPE_UNICODE: |
691 |
|
|
uc = mchars_num2uc(seq + 1, ssz - 1); |
692 |
|
|
break; |
693 |
|
|
case ESCAPE_NUMBERED: |
694 |
|
|
uc = mchars_num2char(seq, ssz); |
695 |
|
|
if (uc < 0) |
696 |
|
|
continue; |
697 |
|
|
break; |
698 |
|
|
case ESCAPE_SPECIAL: |
699 |
|
|
if (p->enc == TERMENC_ASCII) { |
700 |
|
|
rhs = mchars_spec2str(seq, ssz, &rsz); |
701 |
|
|
if (rhs != NULL) |
702 |
|
|
break; |
703 |
|
|
} else { |
704 |
|
|
uc = mchars_spec2cp(seq, ssz); |
705 |
|
|
if (uc > 0) |
706 |
|
|
sz += cond_width(p, uc, &skip); |
707 |
|
|
} |
708 |
|
|
continue; |
709 |
|
|
case ESCAPE_SKIPCHAR: |
710 |
|
|
skip = 1; |
711 |
|
|
continue; |
712 |
|
|
case ESCAPE_OVERSTRIKE: |
713 |
|
|
rsz = 0; |
714 |
|
|
rhs = seq + ssz; |
715 |
|
|
while (seq < rhs) { |
716 |
|
|
if (*seq == '\\') { |
717 |
|
|
mandoc_escape(&seq, NULL, NULL); |
718 |
|
|
continue; |
719 |
|
|
} |
720 |
|
|
i = (*p->width)(p, *seq++); |
721 |
|
|
if (rsz < i) |
722 |
|
|
rsz = i; |
723 |
|
|
} |
724 |
|
|
sz += rsz; |
725 |
|
|
continue; |
726 |
|
|
default: |
727 |
|
|
continue; |
728 |
|
|
} |
729 |
|
|
|
730 |
|
|
/* |
731 |
|
|
* Common handling for Unicode and numbered |
732 |
|
|
* character escape sequences. |
733 |
|
|
*/ |
734 |
|
|
|
735 |
|
|
if (rhs == NULL) { |
736 |
|
|
if (p->enc == TERMENC_ASCII) { |
737 |
|
|
rhs = ascii_uc2str(uc); |
738 |
|
|
rsz = strlen(rhs); |
739 |
|
|
} else { |
740 |
|
|
if ((uc < 0x20 && uc != 0x09) || |
741 |
|
|
(uc > 0x7E && uc < 0xA0)) |
742 |
|
|
uc = 0xFFFD; |
743 |
|
|
sz += cond_width(p, uc, &skip); |
744 |
|
|
continue; |
745 |
|
|
} |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
if (skip) { |
749 |
|
|
skip = 0; |
750 |
|
|
break; |
751 |
|
|
} |
752 |
|
|
|
753 |
|
|
/* |
754 |
|
|
* Common handling for all escape sequences |
755 |
|
|
* printing more than one character. |
756 |
|
|
*/ |
757 |
|
|
|
758 |
|
|
for (i = 0; i < rsz; i++) |
759 |
|
|
sz += (*p->width)(p, *rhs++); |
760 |
|
|
break; |
761 |
|
|
case ASCII_NBRSP: |
762 |
|
|
sz += cond_width(p, ' ', &skip); |
763 |
|
|
cp++; |
764 |
|
|
break; |
765 |
|
|
case ASCII_HYPH: |
766 |
|
|
sz += cond_width(p, '-', &skip); |
767 |
|
|
cp++; |
768 |
|
|
break; |
769 |
|
|
default: |
770 |
|
|
break; |
771 |
|
|
} |
772 |
|
|
} |
773 |
|
|
|
774 |
|
|
return sz; |
775 |
|
|
} |
776 |
|
|
|
777 |
|
|
int |
778 |
|
|
term_vspan(const struct termp *p, const struct roffsu *su) |
779 |
|
|
{ |
780 |
|
|
double r; |
781 |
|
|
int ri; |
782 |
|
|
|
783 |
|
|
switch (su->unit) { |
784 |
|
|
case SCALE_BU: |
785 |
|
|
r = su->scale / 40.0; |
786 |
|
|
break; |
787 |
|
|
case SCALE_CM: |
788 |
|
|
r = su->scale * 6.0 / 2.54; |
789 |
|
|
break; |
790 |
|
|
case SCALE_FS: |
791 |
|
|
r = su->scale * 65536.0 / 40.0; |
792 |
|
|
break; |
793 |
|
|
case SCALE_IN: |
794 |
|
|
r = su->scale * 6.0; |
795 |
|
|
break; |
796 |
|
|
case SCALE_MM: |
797 |
|
|
r = su->scale * 0.006; |
798 |
|
|
break; |
799 |
|
|
case SCALE_PC: |
800 |
|
|
r = su->scale; |
801 |
|
|
break; |
802 |
|
|
case SCALE_PT: |
803 |
|
|
r = su->scale / 12.0; |
804 |
|
|
break; |
805 |
|
|
case SCALE_EN: |
806 |
|
|
case SCALE_EM: |
807 |
|
|
r = su->scale * 0.6; |
808 |
|
|
break; |
809 |
|
|
case SCALE_VS: |
810 |
|
|
r = su->scale; |
811 |
|
|
break; |
812 |
|
|
default: |
813 |
|
|
abort(); |
814 |
|
|
} |
815 |
|
|
ri = r > 0.0 ? r + 0.4995 : r - 0.4995; |
816 |
|
|
return ri < 66 ? ri : 1; |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
/* |
820 |
|
|
* Convert a scaling width to basic units, rounding down. |
821 |
|
|
*/ |
822 |
|
|
int |
823 |
|
|
term_hspan(const struct termp *p, const struct roffsu *su) |
824 |
|
|
{ |
825 |
|
|
|
826 |
|
|
return (*p->hspan)(p, su); |
827 |
|
|
} |