Commit | Line | Data |
---|---|---|
7888c61d | 1 | /* $Id: term.c,v 1.214 2013/12/25 00:39:31 schwarze Exp $ */ |
80387638 | 2 | /* |
36342e81 | 3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
f88b6c16 | 4 | * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org> |
80387638 SW |
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 | #ifdef HAVE_CONFIG_H | |
19 | #include "config.h" | |
20 | #endif | |
21 | ||
22 | #include <sys/types.h> | |
23 | ||
24 | #include <assert.h> | |
25 | #include <ctype.h> | |
26 | #include <stdint.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | ||
31 | #include "mandoc.h" | |
80387638 SW |
32 | #include "out.h" |
33 | #include "term.h" | |
34 | #include "main.h" | |
35 | ||
f88b6c16 FF |
36 | static size_t cond_width(const struct termp *, int, int *); |
37 | static void adjbuf(struct termp *p, size_t); | |
a4c7eb57 SW |
38 | static void bufferc(struct termp *, char); |
39 | static void encode(struct termp *, const char *, size_t); | |
40 | static void encode1(struct termp *, int); | |
80387638 SW |
41 | |
42 | void | |
43 | term_free(struct termp *p) | |
44 | { | |
45 | ||
46 | if (p->buf) | |
47 | free(p->buf); | |
48 | if (p->symtab) | |
a4c7eb57 | 49 | mchars_free(p->symtab); |
80387638 SW |
50 | |
51 | free(p); | |
52 | } | |
53 | ||
54 | ||
55 | void | |
56 | term_begin(struct termp *p, term_margin head, | |
57 | term_margin foot, const void *arg) | |
58 | { | |
59 | ||
60 | p->headf = head; | |
61 | p->footf = foot; | |
62 | p->argf = arg; | |
63 | (*p->begin)(p); | |
64 | } | |
65 | ||
66 | ||
67 | void | |
68 | term_end(struct termp *p) | |
69 | { | |
70 | ||
71 | (*p->end)(p); | |
72 | } | |
73 | ||
80387638 SW |
74 | /* |
75 | * Flush a line of text. A "line" is loosely defined as being something | |
76 | * that should be followed by a newline, regardless of whether it's | |
77 | * broken apart by newlines getting there. A line can also be a | |
78 | * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does | |
79 | * not have a trailing newline. | |
80 | * | |
81 | * The following flags may be specified: | |
82 | * | |
80387638 | 83 | * - TERMP_NOBREAK: this is the most important and is used when making |
36342e81 SW |
84 | * columns. In short: don't print a newline and instead expect the |
85 | * next call to do the padding up to the start of the next column. | |
7888c61d FF |
86 | * p->trailspace may be set to 0, 1, or 2, depending on how many |
87 | * space characters are required at the end of the column. | |
80387638 SW |
88 | * |
89 | * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and | |
90 | * the line is overrun, and don't pad-right if it's underrun. | |
91 | * | |
92 | * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when | |
36342e81 | 93 | * overrunning, instead save the position and continue at that point |
80387638 SW |
94 | * when the next invocation. |
95 | * | |
96 | * In-line line breaking: | |
97 | * | |
98 | * If TERMP_NOBREAK is specified and the line overruns the right | |
99 | * margin, it will break and pad-right to the right margin after | |
100 | * writing. If maxrmargin is violated, it will break and continue | |
101 | * writing from the right-margin, which will lead to the above scenario | |
102 | * upon exit. Otherwise, the line will break at the right margin. | |
103 | */ | |
104 | void | |
105 | term_flushln(struct termp *p) | |
106 | { | |
f88b6c16 FF |
107 | size_t i; /* current input position in p->buf */ |
108 | int ntab; /* number of tabs to prepend */ | |
80387638 SW |
109 | size_t vis; /* current visual position on output */ |
110 | size_t vbl; /* number of blanks to prepend to output */ | |
111 | size_t vend; /* end of word visual position on output */ | |
112 | size_t bp; /* visual right border position */ | |
113 | size_t dv; /* temporary for visual pos calculations */ | |
f88b6c16 FF |
114 | size_t j; /* temporary loop index for p->buf */ |
115 | size_t jhy; /* last hyph before overflow w/r/t j */ | |
80387638 SW |
116 | size_t maxvis; /* output position of visible boundary */ |
117 | size_t mmax; /* used in calculating bp */ | |
118 | ||
119 | /* | |
120 | * First, establish the maximum columns of "visible" content. | |
121 | * This is usually the difference between the right-margin and | |
122 | * an indentation, but can be, for tagged lists or columns, a | |
7888c61d FF |
123 | * small set of values. |
124 | * | |
125 | * The following unsigned-signed subtractions look strange, | |
126 | * but they are actually correct. If the int p->overstep | |
127 | * is negative, it gets sign extended. Subtracting that | |
128 | * very large size_t effectively adds a small number to dv. | |
80387638 SW |
129 | */ |
130 | assert (p->rmargin >= p->offset); | |
131 | dv = p->rmargin - p->offset; | |
132 | maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; | |
133 | dv = p->maxrmargin - p->offset; | |
134 | mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; | |
135 | ||
136 | bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | |
137 | ||
138 | /* | |
36342e81 | 139 | * Calculate the required amount of padding. |
80387638 | 140 | */ |
36342e81 SW |
141 | vbl = p->offset + p->overstep > p->viscol ? |
142 | p->offset + p->overstep - p->viscol : 0; | |
80387638 SW |
143 | |
144 | vis = vend = 0; | |
145 | i = 0; | |
146 | ||
a4c7eb57 | 147 | while (i < p->col) { |
80387638 SW |
148 | /* |
149 | * Handle literal tab characters: collapse all | |
150 | * subsequent tabs into a single huge set of spaces. | |
151 | */ | |
f88b6c16 | 152 | ntab = 0; |
a4c7eb57 | 153 | while (i < p->col && '\t' == p->buf[i]) { |
80387638 SW |
154 | vend = (vis / p->tabwidth + 1) * p->tabwidth; |
155 | vbl += vend - vis; | |
156 | vis = vend; | |
f88b6c16 | 157 | ntab++; |
80387638 SW |
158 | i++; |
159 | } | |
160 | ||
161 | /* | |
162 | * Count up visible word characters. Control sequences | |
163 | * (starting with the CSI) aren't counted. A space | |
164 | * generates a non-printing word, which is valid (the | |
165 | * space is printed according to regular spacing rules). | |
166 | */ | |
167 | ||
a4c7eb57 | 168 | for (j = i, jhy = 0; j < p->col; j++) { |
f88b6c16 | 169 | if (' ' == p->buf[j] || '\t' == p->buf[j]) |
80387638 SW |
170 | break; |
171 | ||
172 | /* Back over the the last printed character. */ | |
173 | if (8 == p->buf[j]) { | |
174 | assert(j); | |
175 | vend -= (*p->width)(p, p->buf[j - 1]); | |
176 | continue; | |
177 | } | |
178 | ||
179 | /* Regular word. */ | |
180 | /* Break at the hyphen point if we overrun. */ | |
181 | if (vend > vis && vend < bp && | |
182 | ASCII_HYPH == p->buf[j]) | |
183 | jhy = j; | |
184 | ||
185 | vend += (*p->width)(p, p->buf[j]); | |
186 | } | |
187 | ||
188 | /* | |
189 | * Find out whether we would exceed the right margin. | |
190 | * If so, break to the next line. | |
191 | */ | |
192 | if (vend > bp && 0 == jhy && vis > 0) { | |
193 | vend -= vis; | |
194 | (*p->endline)(p); | |
36342e81 | 195 | p->viscol = 0; |
80387638 | 196 | if (TERMP_NOBREAK & p->flags) { |
36342e81 | 197 | vbl = p->rmargin; |
80387638 | 198 | vend += p->rmargin - p->offset; |
36342e81 | 199 | } else |
80387638 | 200 | vbl = p->offset; |
80387638 | 201 | |
f88b6c16 FF |
202 | /* use pending tabs on the new line */ |
203 | ||
204 | if (0 < ntab) | |
205 | vbl += ntab * p->tabwidth; | |
206 | ||
7888c61d FF |
207 | /* |
208 | * Remove the p->overstep width. | |
209 | * Again, if p->overstep is negative, | |
210 | * sign extension does the right thing. | |
211 | */ | |
80387638 SW |
212 | |
213 | bp += (size_t)p->overstep; | |
214 | p->overstep = 0; | |
215 | } | |
216 | ||
217 | /* Write out the [remaining] word. */ | |
a4c7eb57 | 218 | for ( ; i < p->col; i++) { |
80387638 SW |
219 | if (vend > bp && jhy > 0 && i > jhy) |
220 | break; | |
221 | if ('\t' == p->buf[i]) | |
222 | break; | |
223 | if (' ' == p->buf[i]) { | |
224 | j = i; | |
225 | while (' ' == p->buf[i]) | |
226 | i++; | |
f88b6c16 | 227 | dv = (i - j) * (*p->width)(p, ' '); |
80387638 SW |
228 | vbl += dv; |
229 | vend += dv; | |
230 | break; | |
231 | } | |
232 | if (ASCII_NBRSP == p->buf[i]) { | |
233 | vbl += (*p->width)(p, ' '); | |
234 | continue; | |
235 | } | |
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 | if (ASCII_HYPH == p->buf[i]) { | |
249 | (*p->letter)(p, '-'); | |
250 | p->viscol += (*p->width)(p, '-'); | |
36342e81 | 251 | continue; |
80387638 | 252 | } |
36342e81 SW |
253 | |
254 | (*p->letter)(p, p->buf[i]); | |
255 | if (8 == p->buf[i]) | |
256 | p->viscol -= (*p->width)(p, p->buf[i-1]); | |
257 | else | |
258 | p->viscol += (*p->width)(p, p->buf[i]); | |
80387638 SW |
259 | } |
260 | vis = vend; | |
261 | } | |
262 | ||
263 | /* | |
264 | * If there was trailing white space, it was not printed; | |
265 | * so reset the cursor position accordingly. | |
266 | */ | |
36342e81 SW |
267 | if (vis) |
268 | vis -= vbl; | |
80387638 SW |
269 | |
270 | p->col = 0; | |
271 | p->overstep = 0; | |
272 | ||
273 | if ( ! (TERMP_NOBREAK & p->flags)) { | |
274 | p->viscol = 0; | |
275 | (*p->endline)(p); | |
276 | return; | |
277 | } | |
278 | ||
279 | if (TERMP_HANG & p->flags) { | |
7888c61d FF |
280 | p->overstep = (int)(vis - maxvis + |
281 | p->trailspace * (*p->width)(p, ' ')); | |
80387638 SW |
282 | |
283 | /* | |
80387638 SW |
284 | * If we have overstepped the margin, temporarily move |
285 | * it to the right and flag the rest of the line to be | |
286 | * shorter. | |
7888c61d FF |
287 | * If there is a request to keep the columns together, |
288 | * allow negative overstep when the column is not full. | |
80387638 | 289 | */ |
7888c61d | 290 | if (p->trailspace && p->overstep < 0) |
80387638 | 291 | p->overstep = 0; |
36342e81 | 292 | return; |
80387638 SW |
293 | |
294 | } else if (TERMP_DANGLE & p->flags) | |
295 | return; | |
296 | ||
36342e81 | 297 | /* If the column was overrun, break the line. */ |
7888c61d | 298 | if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { |
80387638 | 299 | (*p->endline)(p); |
36342e81 | 300 | p->viscol = 0; |
80387638 SW |
301 | } |
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; | |
36342e81 SW |
315 | if (p->col || p->viscol) |
316 | term_flushln(p); | |
80387638 SW |
317 | } |
318 | ||
319 | ||
320 | /* | |
321 | * Asserts a vertical space (a full, empty line-break between lines). | |
322 | * Note that if used twice, this will cause two blank spaces and so on. | |
323 | * All data in the output buffer is flushed prior to the newline | |
324 | * assertion. | |
325 | */ | |
326 | void | |
327 | term_vspace(struct termp *p) | |
328 | { | |
329 | ||
330 | term_newln(p); | |
331 | p->viscol = 0; | |
f88b6c16 FF |
332 | if (0 < p->skipvsp) |
333 | p->skipvsp--; | |
334 | else | |
335 | (*p->endline)(p); | |
80387638 SW |
336 | } |
337 | ||
80387638 SW |
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 | ||
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 | ||
358 | void | |
359 | term_fontpush(struct termp *p, enum termfont f) | |
360 | { | |
361 | ||
362 | assert(p->fonti + 1 < 10); | |
363 | p->fontl = p->fontq[p->fonti]; | |
364 | p->fontq[++p->fonti] = f; | |
365 | } | |
366 | ||
367 | ||
368 | const void * | |
369 | term_fontq(struct termp *p) | |
370 | { | |
371 | ||
372 | return(&p->fontq[p->fonti]); | |
373 | } | |
374 | ||
375 | ||
376 | enum termfont | |
377 | term_fonttop(struct termp *p) | |
378 | { | |
379 | ||
380 | return(p->fontq[p->fonti]); | |
381 | } | |
382 | ||
383 | ||
384 | void | |
385 | term_fontpopq(struct termp *p, const void *key) | |
386 | { | |
387 | ||
f88b6c16 | 388 | while (p->fonti >= 0 && key < (void *)(p->fontq + p->fonti)) |
80387638 SW |
389 | p->fonti--; |
390 | assert(p->fonti >= 0); | |
391 | } | |
392 | ||
393 | ||
394 | void | |
395 | term_fontpop(struct termp *p) | |
396 | { | |
397 | ||
398 | assert(p->fonti); | |
399 | p->fonti--; | |
400 | } | |
401 | ||
80387638 SW |
402 | /* |
403 | * Handle pwords, partial words, which may be either a single word or a | |
404 | * phrase that cannot be broken down (such as a literal string). This | |
405 | * handles word styling. | |
406 | */ | |
407 | void | |
408 | term_word(struct termp *p, const char *word) | |
409 | { | |
7888c61d | 410 | const char nbrsp[2] = { ASCII_NBRSP, 0 }; |
a4c7eb57 SW |
411 | const char *seq, *cp; |
412 | char c; | |
413 | int sz, uc; | |
80387638 | 414 | size_t ssz; |
a4c7eb57 | 415 | enum mandoc_esc esc; |
80387638 | 416 | |
80387638 SW |
417 | if ( ! (TERMP_NOSPACE & p->flags)) { |
418 | if ( ! (TERMP_KEEP & p->flags)) { | |
80387638 SW |
419 | bufferc(p, ' '); |
420 | if (TERMP_SENTENCE & p->flags) | |
421 | bufferc(p, ' '); | |
422 | } else | |
423 | bufferc(p, ASCII_NBRSP); | |
424 | } | |
f88b6c16 FF |
425 | if (TERMP_PREKEEP & p->flags) |
426 | p->flags |= TERMP_KEEP; | |
80387638 SW |
427 | |
428 | if ( ! (p->flags & TERMP_NONOSPACE)) | |
429 | p->flags &= ~TERMP_NOSPACE; | |
430 | else | |
431 | p->flags |= TERMP_NOSPACE; | |
432 | ||
7888c61d | 433 | p->flags &= ~TERMP_SENTENCE; |
80387638 | 434 | |
a4c7eb57 | 435 | while ('\0' != *word) { |
f88b6c16 FF |
436 | if ('\\' != *word) { |
437 | if (TERMP_SKIPCHAR & p->flags) { | |
438 | p->flags &= ~TERMP_SKIPCHAR; | |
439 | word++; | |
440 | continue; | |
441 | } | |
7888c61d FF |
442 | if (TERMP_NBRWORD & p->flags) { |
443 | if (' ' == *word) { | |
444 | encode(p, nbrsp, 1); | |
445 | word++; | |
446 | continue; | |
447 | } | |
448 | ssz = strcspn(word, "\\ "); | |
449 | } else | |
450 | ssz = strcspn(word, "\\"); | |
80387638 | 451 | encode(p, word, ssz); |
f88b6c16 | 452 | word += (int)ssz; |
80387638 | 453 | continue; |
f88b6c16 | 454 | } |
80387638 | 455 | |
a4c7eb57 SW |
456 | word++; |
457 | esc = mandoc_escape(&word, &seq, &sz); | |
458 | if (ESCAPE_ERROR == esc) | |
459 | break; | |
460 | ||
461 | if (TERMENC_ASCII != p->enc) | |
462 | switch (esc) { | |
463 | case (ESCAPE_UNICODE): | |
464 | uc = mchars_num2uc(seq + 1, sz - 1); | |
465 | if ('\0' == uc) | |
466 | break; | |
467 | encode1(p, uc); | |
468 | continue; | |
469 | case (ESCAPE_SPECIAL): | |
470 | uc = mchars_spec2cp(p->symtab, seq, sz); | |
471 | if (uc <= 0) | |
472 | break; | |
473 | encode1(p, uc); | |
474 | continue; | |
475 | default: | |
476 | break; | |
477 | } | |
80387638 | 478 | |
a4c7eb57 SW |
479 | switch (esc) { |
480 | case (ESCAPE_UNICODE): | |
481 | encode1(p, '?'); | |
60e1e752 | 482 | break; |
a4c7eb57 SW |
483 | case (ESCAPE_NUMBERED): |
484 | c = mchars_num2char(seq, sz); | |
485 | if ('\0' != c) | |
486 | encode(p, &c, 1); | |
80387638 | 487 | break; |
a4c7eb57 SW |
488 | case (ESCAPE_SPECIAL): |
489 | cp = mchars_spec2str(p->symtab, seq, sz, &ssz); | |
490 | if (NULL != cp) | |
491 | encode(p, cp, ssz); | |
492 | else if (1 == ssz) | |
493 | encode(p, seq, sz); | |
80387638 | 494 | break; |
a4c7eb57 | 495 | case (ESCAPE_FONTBOLD): |
80387638 SW |
496 | term_fontrepl(p, TERMFONT_BOLD); |
497 | break; | |
a4c7eb57 | 498 | case (ESCAPE_FONTITALIC): |
80387638 SW |
499 | term_fontrepl(p, TERMFONT_UNDER); |
500 | break; | |
f88b6c16 FF |
501 | case (ESCAPE_FONTBI): |
502 | term_fontrepl(p, TERMFONT_BI); | |
503 | break; | |
a4c7eb57 SW |
504 | case (ESCAPE_FONT): |
505 | /* FALLTHROUGH */ | |
506 | case (ESCAPE_FONTROMAN): | |
80387638 SW |
507 | term_fontrepl(p, TERMFONT_NONE); |
508 | break; | |
a4c7eb57 | 509 | case (ESCAPE_FONTPREV): |
80387638 SW |
510 | term_fontlast(p); |
511 | break; | |
a4c7eb57 | 512 | case (ESCAPE_NOSPACE): |
f88b6c16 FF |
513 | if (TERMP_SKIPCHAR & p->flags) |
514 | p->flags &= ~TERMP_SKIPCHAR; | |
515 | else if ('\0' == *word) | |
a4c7eb57 SW |
516 | p->flags |= TERMP_NOSPACE; |
517 | break; | |
f88b6c16 FF |
518 | case (ESCAPE_SKIPCHAR): |
519 | p->flags |= TERMP_SKIPCHAR; | |
520 | break; | |
80387638 SW |
521 | default: |
522 | break; | |
523 | } | |
80387638 | 524 | } |
7888c61d | 525 | p->flags &= ~TERMP_NBRWORD; |
80387638 SW |
526 | } |
527 | ||
80387638 | 528 | static void |
f88b6c16 | 529 | adjbuf(struct termp *p, size_t sz) |
80387638 SW |
530 | { |
531 | ||
532 | if (0 == p->maxcols) | |
533 | p->maxcols = 1024; | |
534 | while (sz >= p->maxcols) | |
535 | p->maxcols <<= 2; | |
536 | ||
f88b6c16 | 537 | p->buf = mandoc_realloc(p->buf, sizeof(int) * p->maxcols); |
80387638 SW |
538 | } |
539 | ||
80387638 SW |
540 | static void |
541 | bufferc(struct termp *p, char c) | |
542 | { | |
543 | ||
544 | if (p->col + 1 >= p->maxcols) | |
545 | adjbuf(p, p->col + 1); | |
546 | ||
a4c7eb57 | 547 | p->buf[p->col++] = c; |
80387638 SW |
548 | } |
549 | ||
a4c7eb57 SW |
550 | /* |
551 | * See encode(). | |
552 | * Do this for a single (probably unicode) value. | |
553 | * Does not check for non-decorated glyphs. | |
554 | */ | |
555 | static void | |
556 | encode1(struct termp *p, int c) | |
557 | { | |
558 | enum termfont f; | |
559 | ||
f88b6c16 FF |
560 | if (TERMP_SKIPCHAR & p->flags) { |
561 | p->flags &= ~TERMP_SKIPCHAR; | |
562 | return; | |
563 | } | |
564 | ||
565 | if (p->col + 6 >= p->maxcols) | |
566 | adjbuf(p, p->col + 6); | |
a4c7eb57 SW |
567 | |
568 | f = term_fonttop(p); | |
569 | ||
f88b6c16 | 570 | if (TERMFONT_UNDER == f || TERMFONT_BI == f) { |
a4c7eb57 | 571 | p->buf[p->col++] = '_'; |
f88b6c16 FF |
572 | p->buf[p->col++] = 8; |
573 | } | |
574 | if (TERMFONT_BOLD == f || TERMFONT_BI == f) { | |
575 | if (ASCII_HYPH == c) | |
576 | p->buf[p->col++] = '-'; | |
577 | else | |
578 | p->buf[p->col++] = c; | |
579 | p->buf[p->col++] = 8; | |
580 | } | |
a4c7eb57 SW |
581 | p->buf[p->col++] = c; |
582 | } | |
80387638 SW |
583 | |
584 | static void | |
585 | encode(struct termp *p, const char *word, size_t sz) | |
586 | { | |
f88b6c16 | 587 | size_t i; |
a4c7eb57 | 588 | |
f88b6c16 FF |
589 | if (TERMP_SKIPCHAR & p->flags) { |
590 | p->flags &= ~TERMP_SKIPCHAR; | |
591 | return; | |
592 | } | |
80387638 SW |
593 | |
594 | /* | |
595 | * Encode and buffer a string of characters. If the current | |
596 | * font mode is unset, buffer directly, else encode then buffer | |
597 | * character by character. | |
598 | */ | |
599 | ||
f88b6c16 FF |
600 | if (TERMFONT_NONE == term_fonttop(p)) { |
601 | if (p->col + sz >= p->maxcols) | |
602 | adjbuf(p, p->col + sz); | |
603 | for (i = 0; i < sz; i++) | |
a4c7eb57 | 604 | p->buf[p->col++] = word[i]; |
80387638 SW |
605 | return; |
606 | } | |
607 | ||
608 | /* Pre-buffer, assuming worst-case. */ | |
609 | ||
f88b6c16 FF |
610 | if (p->col + 1 + (sz * 5) >= p->maxcols) |
611 | adjbuf(p, p->col + 1 + (sz * 5)); | |
80387638 | 612 | |
f88b6c16 FF |
613 | for (i = 0; i < sz; i++) { |
614 | if (ASCII_HYPH == word[i] || | |
615 | isgraph((unsigned char)word[i])) | |
616 | encode1(p, word[i]); | |
80387638 | 617 | else |
a4c7eb57 | 618 | p->buf[p->col++] = word[i]; |
80387638 SW |
619 | } |
620 | } | |
621 | ||
80387638 SW |
622 | size_t |
623 | term_len(const struct termp *p, size_t sz) | |
624 | { | |
625 | ||
626 | return((*p->width)(p, ' ') * sz); | |
627 | } | |
628 | ||
f88b6c16 FF |
629 | static size_t |
630 | cond_width(const struct termp *p, int c, int *skip) | |
631 | { | |
632 | ||
633 | if (*skip) { | |
634 | (*skip) = 0; | |
635 | return(0); | |
636 | } else | |
637 | return((*p->width)(p, c)); | |
638 | } | |
80387638 SW |
639 | |
640 | size_t | |
641 | term_strlen(const struct termp *p, const char *cp) | |
642 | { | |
a4c7eb57 | 643 | size_t sz, rsz, i; |
f88b6c16 | 644 | int ssz, skip, c; |
80387638 | 645 | const char *seq, *rhs; |
a4c7eb57 SW |
646 | enum mandoc_esc esc; |
647 | static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' }; | |
80387638 | 648 | |
a4c7eb57 SW |
649 | /* |
650 | * Account for escaped sequences within string length | |
651 | * calculations. This follows the logic in term_word() as we | |
652 | * must calculate the width of produced strings. | |
653 | */ | |
80387638 | 654 | |
a4c7eb57 | 655 | sz = 0; |
f88b6c16 | 656 | skip = 0; |
a4c7eb57 SW |
657 | while ('\0' != *cp) { |
658 | rsz = strcspn(cp, rej); | |
659 | for (i = 0; i < rsz; i++) | |
f88b6c16 | 660 | sz += cond_width(p, *cp++, &skip); |
a4c7eb57 SW |
661 | |
662 | c = 0; | |
663 | switch (*cp) { | |
664 | case ('\\'): | |
665 | cp++; | |
666 | esc = mandoc_escape(&cp, &seq, &ssz); | |
667 | if (ESCAPE_ERROR == esc) | |
668 | return(sz); | |
669 | ||
670 | if (TERMENC_ASCII != p->enc) | |
671 | switch (esc) { | |
672 | case (ESCAPE_UNICODE): | |
673 | c = mchars_num2uc | |
674 | (seq + 1, ssz - 1); | |
675 | if ('\0' == c) | |
676 | break; | |
f88b6c16 | 677 | sz += cond_width(p, c, &skip); |
a4c7eb57 SW |
678 | continue; |
679 | case (ESCAPE_SPECIAL): | |
680 | c = mchars_spec2cp | |
681 | (p->symtab, seq, ssz); | |
682 | if (c <= 0) | |
683 | break; | |
f88b6c16 | 684 | sz += cond_width(p, c, &skip); |
a4c7eb57 SW |
685 | continue; |
686 | default: | |
687 | break; | |
688 | } | |
689 | ||
690 | rhs = NULL; | |
691 | ||
692 | switch (esc) { | |
693 | case (ESCAPE_UNICODE): | |
f88b6c16 | 694 | sz += cond_width(p, '?', &skip); |
80387638 | 695 | break; |
a4c7eb57 SW |
696 | case (ESCAPE_NUMBERED): |
697 | c = mchars_num2char(seq, ssz); | |
698 | if ('\0' != c) | |
f88b6c16 | 699 | sz += cond_width(p, c, &skip); |
a4c7eb57 SW |
700 | break; |
701 | case (ESCAPE_SPECIAL): | |
702 | rhs = mchars_spec2str | |
80387638 SW |
703 | (p->symtab, seq, ssz, &rsz); |
704 | ||
a4c7eb57 | 705 | if (ssz != 1 || rhs) |
80387638 SW |
706 | break; |
707 | ||
708 | rhs = seq; | |
709 | rsz = ssz; | |
710 | break; | |
f88b6c16 FF |
711 | case (ESCAPE_SKIPCHAR): |
712 | skip = 1; | |
713 | break; | |
80387638 | 714 | default: |
80387638 SW |
715 | break; |
716 | } | |
717 | ||
a4c7eb57 SW |
718 | if (NULL == rhs) |
719 | break; | |
720 | ||
f88b6c16 FF |
721 | if (skip) { |
722 | skip = 0; | |
723 | break; | |
724 | } | |
725 | ||
a4c7eb57 SW |
726 | for (i = 0; i < rsz; i++) |
727 | sz += (*p->width)(p, *rhs++); | |
728 | break; | |
729 | case (ASCII_NBRSP): | |
f88b6c16 | 730 | sz += cond_width(p, ' ', &skip); |
80387638 | 731 | cp++; |
a4c7eb57 SW |
732 | break; |
733 | case (ASCII_HYPH): | |
f88b6c16 | 734 | sz += cond_width(p, '-', &skip); |
80387638 | 735 | cp++; |
a4c7eb57 SW |
736 | break; |
737 | default: | |
738 | break; | |
739 | } | |
740 | } | |
80387638 SW |
741 | |
742 | return(sz); | |
743 | } | |
744 | ||
80387638 SW |
745 | /* ARGSUSED */ |
746 | size_t | |
747 | term_vspan(const struct termp *p, const struct roffsu *su) | |
748 | { | |
749 | double r; | |
750 | ||
751 | switch (su->unit) { | |
752 | case (SCALE_CM): | |
753 | r = su->scale * 2; | |
754 | break; | |
755 | case (SCALE_IN): | |
756 | r = su->scale * 6; | |
757 | break; | |
758 | case (SCALE_PC): | |
759 | r = su->scale; | |
760 | break; | |
761 | case (SCALE_PT): | |
762 | r = su->scale / 8; | |
763 | break; | |
764 | case (SCALE_MM): | |
765 | r = su->scale / 1000; | |
766 | break; | |
767 | case (SCALE_VS): | |
768 | r = su->scale; | |
769 | break; | |
770 | default: | |
771 | r = su->scale - 1; | |
772 | break; | |
773 | } | |
774 | ||
775 | if (r < 0.0) | |
776 | r = 0.0; | |
777 | return(/* LINTED */(size_t) | |
778 | r); | |
779 | } | |
780 | ||
80387638 SW |
781 | size_t |
782 | term_hspan(const struct termp *p, const struct roffsu *su) | |
783 | { | |
784 | double v; | |
785 | ||
786 | v = ((*p->hspan)(p, su)); | |
787 | if (v < 0.0) | |
788 | v = 0.0; | |
789 | return((size_t) /* LINTED */ | |
790 | v); | |
791 | } |