| GCC Code Coverage Report | |||||||||||||||||||||
| 
 | |||||||||||||||||||||
| Line | Branch | Exec | Source | 
| 1 | /* $OpenBSD: term.c,v 1.134 2017/07/28 14:24:17 florian Exp $ */ | ||
| 2 | /* | ||
| 3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | ||
| 4 | * Copyright (c) 2010-2017 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_col *, 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 | static void endline(struct termp *); | ||
| 38 | |||
| 39 | |||
| 40 | void | ||
| 41 | term_setcol(struct termp *p, size_t maxtcol) | ||
| 42 | { | ||
| 43 | ✓✓ | 84996 | 	if (maxtcol > p->maxtcol) { | 
| 44 | 423 | p->tcols = mandoc_recallocarray(p->tcols, | |
| 45 | p->maxtcol, maxtcol, sizeof(*p->tcols)); | ||
| 46 | 423 | p->maxtcol = maxtcol; | |
| 47 | 423 | } | |
| 48 | 42498 | p->lasttcol = maxtcol - 1; | |
| 49 | 42498 | p->tcol = p->tcols; | |
| 50 | 42498 | } | |
| 51 | |||
| 52 | void | ||
| 53 | term_free(struct termp *p) | ||
| 54 | { | ||
| 55 | ✓✓ | 34297 | for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++) | 
| 56 | 7529 | free(p->tcol->buf); | |
| 57 | 6413 | free(p->tcols); | |
| 58 | 6413 | free(p->fontq); | |
| 59 | 6413 | free(p); | |
| 60 | 6413 | } | |
| 61 | |||
| 62 | void | ||
| 63 | term_begin(struct termp *p, term_margin head, | ||
| 64 | term_margin foot, const struct roff_meta *arg) | ||
| 65 | { | ||
| 66 | |||
| 67 | 12826 | p->headf = head; | |
| 68 | 6413 | p->footf = foot; | |
| 69 | 6413 | p->argf = arg; | |
| 70 | 6413 | (*p->begin)(p); | |
| 71 | 6413 | } | |
| 72 | |||
| 73 | void | ||
| 74 | term_end(struct termp *p) | ||
| 75 | { | ||
| 76 | |||
| 77 | 12826 | (*p->end)(p); | |
| 78 | 6413 | } | |
| 79 | |||
| 80 | /* | ||
| 81 | * Flush a chunk of text. By default, break the output line each time | ||
| 82 | * the right margin is reached, and continue output on the next line | ||
| 83 | * at the same offset as the chunk itself. By default, also break the | ||
| 84 | * output line at the end of the chunk. | ||
| 85 | * The following flags may be specified: | ||
| 86 | * | ||
| 87 | * - TERMP_NOBREAK: Do not break the output line at the right margin, | ||
| 88 | * but only at the max right margin. Also, do not break the output | ||
| 89 | * line at the end of the chunk, such that the next call can pad to | ||
| 90 | * the next column. However, if less than p->trailspace blanks, | ||
| 91 | * which can be 0, 1, or 2, remain to the right margin, the line | ||
| 92 | * will be broken. | ||
| 93 | * - TERMP_BRTRSP: Consider trailing whitespace significant | ||
| 94 | * when deciding whether the chunk fits or not. | ||
| 95 | * - TERMP_BRIND: If the chunk does not fit and the output line has | ||
| 96 | * to be broken, start the next line at the right margin instead | ||
| 97 | * of at the offset. Used together with TERMP_NOBREAK for the tags | ||
| 98 | * in various kinds of tagged lists. | ||
| 99 | * - TERMP_HANG: Do not break the output line at the right margin, | ||
| 100 | * append the next chunk after it even if this one is too long. | ||
| 101 | * To be used together with TERMP_NOBREAK. | ||
| 102 | * - TERMP_NOPAD: Start writing at the current position, | ||
| 103 | * do not pad with blank characters up to the offset. | ||
| 104 | */ | ||
| 105 | void | ||
| 106 | term_flushln(struct termp *p) | ||
| 107 | { | ||
| 108 | size_t vis; /* current visual position on output */ | ||
| 109 | size_t vbl; /* number of blanks to prepend to output */ | ||
| 110 | size_t vend; /* end of word visual position on output */ | ||
| 111 | size_t bp; /* visual right border position */ | ||
| 112 | size_t dv; /* temporary for visual pos calculations */ | ||
| 113 | size_t j; /* temporary loop index for p->tcol->buf */ | ||
| 114 | size_t jhy; /* last hyph before overflow w/r/t j */ | ||
| 115 | size_t maxvis; /* output position of visible boundary */ | ||
| 116 | int ntab; /* number of tabs to prepend */ | ||
| 117 | int breakline; /* after this word */ | ||
| 118 | |||
| 119 | ✓✓✓✓ | 933724 | vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ? | 
| 120 | 186203 | 0 : p->tcol->offset - p->viscol; | |
| 121 | ✓✓✓✓ | 217138 | if (p->minbl && vbl < p->minbl) | 
| 122 | 342 | vbl = p->minbl; | |
| 123 | ✓✓ | 560310 | maxvis = p->tcol->rmargin > p->viscol + vbl ? | 
| 124 | 186338 | p->tcol->rmargin - p->viscol - vbl : 0; | |
| 125 | ✓✓ | 519720 | bp = !(p->flags & TERMP_NOBREAK) ? maxvis : | 
| 126 | ✓✓ | 82458 | p->maxrmargin > p->viscol + vbl ? | 
| 127 | 41220 | p->maxrmargin - p->viscol - vbl : 0; | |
| 128 | vis = vend = 0; | ||
| 129 | |||
| 130 | ✓✓ | 186986 | if ((p->flags & TERMP_MULTICOL) == 0) | 
| 131 | 131663 | p->tcol->col = 0; | |
| 132 | ✓✓ | 720638 | 	while (p->tcol->col < p->tcol->lastcol) { | 
| 133 | |||
| 134 | /* | ||
| 135 | * Handle literal tab characters: collapse all | ||
| 136 | * subsequent tabs into a single huge set of spaces. | ||
| 137 | */ | ||
| 138 | |||
| 139 | ntab = 0; | ||
| 140 | ✓✗✓✓ | 1623609 | while (p->tcol->col < p->tcol->lastcol && | 
| 141 | 541203 | 		    p->tcol->buf[p->tcol->col] == '\t') { | |
| 142 | 7452 | vend = term_tab_next(vis); | |
| 143 | 7452 | vbl += vend - vis; | |
| 144 | vis = vend; | ||
| 145 | 7452 | ntab++; | |
| 146 | 7452 | p->tcol->col++; | |
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | * Count up visible word characters. Control sequences | ||
| 151 | * (starting with the CSI) aren't counted. A space | ||
| 152 | * generates a non-printing word, which is valid (the | ||
| 153 | * space is printed according to regular spacing rules). | ||
| 154 | */ | ||
| 155 | |||
| 156 | jhy = 0; | ||
| 157 | breakline = 0; | ||
| 158 | ✓✓ | 7176382 | 		for (j = p->tcol->col; j < p->tcol->lastcol; j++) { | 
| 159 | ✓✓ | 3401718 | 			if (p->tcol->buf[j] == '\n') { | 
| 160 | ✓✗ | 36 | if ((p->flags & TERMP_BRIND) == 0) | 
| 161 | 36 | breakline = 1; | |
| 162 | continue; | ||
| 163 | } | ||
| 164 | ✓✓✓✓ | 6463124 | if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t') | 
| 165 | break; | ||
| 166 | |||
| 167 | /* Back over the last printed character. */ | ||
| 168 | ✓✓ | 3054404 | 			if (p->tcol->buf[j] == '\b') { | 
| 169 | ✗✓ | 291573 | assert(j); | 
| 170 | 291573 | vend -= (*p->width)(p, p->tcol->buf[j - 1]); | |
| 171 | 291573 | continue; | |
| 172 | } | ||
| 173 | |||
| 174 | /* Regular word. */ | ||
| 175 | /* Break at the hyphen point if we overrun. */ | ||
| 176 | ✓✓✓✓ ✓✓ | 7092625 | if (vend > vis && vend < bp && | 
| 177 | ✓✓ | 2154917 | (p->tcol->buf[j] == ASCII_HYPH|| | 
| 178 | 2149239 | p->tcol->buf[j] == ASCII_BREAK)) | |
| 179 | 5732 | jhy = j; | |
| 180 | |||
| 181 | /* | ||
| 182 | * Hyphenation now decided, put back a real | ||
| 183 | * hyphen such that we get the correct width. | ||
| 184 | */ | ||
| 185 | ✓✓ | 2762831 | if (p->tcol->buf[j] == ASCII_HYPH) | 
| 186 | 5715 | p->tcol->buf[j] = '-'; | |
| 187 | |||
| 188 | 2762831 | vend += (*p->width)(p, p->tcol->buf[j]); | |
| 189 | 2762831 | } | |
| 190 | |||
| 191 | /* | ||
| 192 | * Find out whether we would exceed the right margin. | ||
| 193 | * If so, break to the next line. | ||
| 194 | */ | ||
| 195 | |||
| 196 | ✓✓✓✓ | 541135 | if (vend > bp && jhy == 0 && vis > 0 && | 
| 197 | 7384 | 		    (p->flags & TERMP_BRNEVER) == 0) { | |
| 198 | ✓✓ | 7285 | if (p->flags & TERMP_MULTICOL) | 
| 199 | 135 | return; | |
| 200 | |||
| 201 | 7150 | endline(p); | |
| 202 | 7150 | vend -= vis; | |
| 203 | |||
| 204 | /* Use pending tabs on the new line. */ | ||
| 205 | |||
| 206 | vbl = 0; | ||
| 207 | ✓✓ | 14336 | while (ntab--) | 
| 208 | 18 | vbl = term_tab_next(vbl); | |
| 209 | |||
| 210 | /* Re-establish indentation. */ | ||
| 211 | |||
| 212 | ✓✓ | 7150 | if (p->flags & TERMP_BRIND) | 
| 213 | 819 | vbl += p->tcol->rmargin; | |
| 214 | else | ||
| 215 | 6331 | vbl += p->tcol->offset; | |
| 216 | ✓✓ | 20460 | maxvis = p->tcol->rmargin > vbl ? | 
| 217 | 6160 | p->tcol->rmargin - vbl : 0; | |
| 218 | ✓✓ | 20604 | bp = !(p->flags & TERMP_NOBREAK) ? maxvis : | 
| 219 | ✓✓ | 1602 | p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; | 
| 220 | 7150 | } | |
| 221 | |||
| 222 | /* | ||
| 223 | * Write out the rest of the word. | ||
| 224 | */ | ||
| 225 | |||
| 226 | ✓✓ | 6639474 | 		for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) { | 
| 227 | ✓✓✓✓ | 3401882 | if (vend > bp && jhy > 0 && p->tcol->col > jhy) | 
| 228 | break; | ||
| 229 | ✓✓ | 3400140 | if (p->tcol->buf[p->tcol->col] == '\n') | 
| 230 | continue; | ||
| 231 | ✓✓ | 3400104 | if (p->tcol->buf[p->tcol->col] == '\t') | 
| 232 | break; | ||
| 233 | ✓✓ | 3393066 | 			if (p->tcol->buf[p->tcol->col] == ' ') { | 
| 234 | j = p->tcol->col; | ||
| 235 | ✓✓✓✓ | 2048817 | while (p->tcol->col < p->tcol->lastcol && | 
| 236 | 682795 | p->tcol->buf[p->tcol->col] == ' ') | |
| 237 | 342766 | p->tcol->col++; | |
| 238 | 340173 | dv = (p->tcol->col - j) * (*p->width)(p, ' '); | |
| 239 | 340173 | vbl += dv; | |
| 240 | 340173 | vend += dv; | |
| 241 | 340173 | break; | |
| 242 | } | ||
| 243 | ✓✓ | 3052893 | 			if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) { | 
| 244 | 21581 | vbl += (*p->width)(p, ' '); | |
| 245 | 21581 | continue; | |
| 246 | } | ||
| 247 | ✓✓ | 3031312 | if (p->tcol->buf[p->tcol->col] == ASCII_BREAK) | 
| 248 | continue; | ||
| 249 | |||
| 250 | /* | ||
| 251 | * Now we definitely know there will be | ||
| 252 | * printable characters to output, | ||
| 253 | * so write preceding white space now. | ||
| 254 | */ | ||
| 255 | ✓✓ | 3031258 | 			if (vbl) { | 
| 256 | 493655 | (*p->advance)(p, vbl); | |
| 257 | 493655 | p->viscol += vbl; | |
| 258 | vbl = 0; | ||
| 259 | 493655 | } | |
| 260 | |||
| 261 | 3031258 | (*p->letter)(p, p->tcol->buf[p->tcol->col]); | |
| 262 | ✓✓ | 3031258 | if (p->tcol->buf[p->tcol->col] == '\b') | 
| 263 | 291429 | p->viscol -= (*p->width)(p, | |
| 264 | 291429 | p->tcol->buf[p->tcol->col - 1]); | |
| 265 | else | ||
| 266 | 2739829 | p->viscol += (*p->width)(p, | |
| 267 | p->tcol->buf[p->tcol->col]); | ||
| 268 | } | ||
| 269 | vis = vend; | ||
| 270 | |||
| 271 | ✓✓ | 533616 | if (breakline == 0) | 
| 272 | continue; | ||
| 273 | |||
| 274 | /* Explicitly requested output line break. */ | ||
| 275 | |||
| 276 | ✗✓ | 36 | if (p->flags & TERMP_MULTICOL) | 
| 277 | return; | ||
| 278 | |||
| 279 | 36 | endline(p); | |
| 280 | breakline = 0; | ||
| 281 | vis = vend = 0; | ||
| 282 | |||
| 283 | /* Re-establish indentation. */ | ||
| 284 | |||
| 285 | 36 | vbl = p->tcol->offset; | |
| 286 | ✓✗ | 108 | maxvis = p->tcol->rmargin > vbl ? | 
| 287 | 36 | p->tcol->rmargin - vbl : 0; | |
| 288 | ✓✗ | 108 | bp = !(p->flags & TERMP_NOBREAK) ? maxvis : | 
| 289 | p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; | ||
| 290 | } | ||
| 291 | |||
| 292 | /* | ||
| 293 | * If there was trailing white space, it was not printed; | ||
| 294 | * so reset the cursor position accordingly. | ||
| 295 | */ | ||
| 296 | |||
| 297 | ✓✓ | 186851 | if (vis > vbl) | 
| 298 | 186275 | vis -= vbl; | |
| 299 | else | ||
| 300 | vis = 0; | ||
| 301 | |||
| 302 | 186851 | p->col = p->tcol->col = p->tcol->lastcol = 0; | |
| 303 | 186851 | p->minbl = p->trailspace; | |
| 304 | 186851 | p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); | |
| 305 | |||
| 306 | ✓✓ | 186851 | if (p->flags & TERMP_MULTICOL) | 
| 307 | 55188 | return; | |
| 308 | |||
| 309 | /* Trailing whitespace is significant in some columns. */ | ||
| 310 | |||
| 311 | ✓✓✓✓ | 131897 | if (vis && vbl && (TERMP_BRTRSP & p->flags)) | 
| 312 | 99 | vis += vbl; | |
| 313 | |||
| 314 | /* If the column was overrun, break the line. */ | ||
| 315 | ✓✓✓✓ | 171101 | if ((p->flags & TERMP_NOBREAK) == 0 || | 
| 316 | ✓✓ | 41238 | ((p->flags & TERMP_HANG) == 0 && | 
| 317 | 39438 | vis + p->trailspace * (*p->width)(p, ' ') > maxvis)) | |
| 318 | 100314 | endline(p); | |
| 319 | 318649 | } | |
| 320 | |||
| 321 | static void | ||
| 322 | endline(struct termp *p) | ||
| 323 | { | ||
| 324 | ✗✓ | 215000 | 	if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) { | 
| 325 | p->mc = NULL; | ||
| 326 | p->flags &= ~TERMP_ENDMC; | ||
| 327 | } | ||
| 328 | ✗✓ | 107500 | 	if (p->mc != NULL) { | 
| 329 | if (p->viscol && p->maxrmargin >= p->viscol) | ||
| 330 | (*p->advance)(p, p->maxrmargin - p->viscol + 1); | ||
| 331 | p->flags |= TERMP_NOBUF | TERMP_NOSPACE; | ||
| 332 | term_word(p, p->mc); | ||
| 333 | p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC); | ||
| 334 | } | ||
| 335 | 107500 | p->viscol = 0; | |
| 336 | 107500 | p->minbl = 0; | |
| 337 | 107500 | (*p->endline)(p); | |
| 338 | 107500 | } | |
| 339 | |||
| 340 | /* | ||
| 341 | * A newline only breaks an existing line; it won't assert vertical | ||
| 342 | * space. All data in the output buffer is flushed prior to the newline | ||
| 343 | * assertion. | ||
| 344 | */ | ||
| 345 | void | ||
| 346 | term_newln(struct termp *p) | ||
| 347 | { | ||
| 348 | |||
| 349 | 256892 | p->flags |= TERMP_NOSPACE; | |
| 350 | ✓✓✓✓ | 198937 | if (p->tcol->lastcol || p->viscol) | 
| 351 | 58171 | term_flushln(p); | |
| 352 | 128446 | } | |
| 353 | |||
| 354 | /* | ||
| 355 | * Asserts a vertical space (a full, empty line-break between lines). | ||
| 356 | * Note that if used twice, this will cause two blank spaces and so on. | ||
| 357 | * All data in the output buffer is flushed prior to the newline | ||
| 358 | * assertion. | ||
| 359 | */ | ||
| 360 | void | ||
| 361 | term_vspace(struct termp *p) | ||
| 362 | { | ||
| 363 | |||
| 364 | 99396 | term_newln(p); | |
| 365 | 49698 | p->viscol = 0; | |
| 366 | 49698 | p->minbl = 0; | |
| 367 | ✓✓ | 49698 | if (0 < p->skipvsp) | 
| 368 | 6885 | p->skipvsp--; | |
| 369 | else | ||
| 370 | 42813 | (*p->endline)(p); | |
| 371 | 49698 | } | |
| 372 | |||
| 373 | /* Swap current and previous font; for \fP and .ft P */ | ||
| 374 | void | ||
| 375 | term_fontlast(struct termp *p) | ||
| 376 | { | ||
| 377 | enum termfont f; | ||
| 378 | |||
| 379 | 1962 | f = p->fontl; | |
| 380 | 981 | p->fontl = p->fontq[p->fonti]; | |
| 381 | 981 | p->fontq[p->fonti] = f; | |
| 382 | 981 | } | |
| 383 | |||
| 384 | /* Set font, save current, discard previous; for \f, .ft, .B etc. */ | ||
| 385 | void | ||
| 386 | term_fontrepl(struct termp *p, enum termfont f) | ||
| 387 | { | ||
| 388 | |||
| 389 | 291106 | p->fontl = p->fontq[p->fonti]; | |
| 390 | 145553 | p->fontq[p->fonti] = f; | |
| 391 | 145553 | } | |
| 392 | |||
| 393 | /* Set font, save previous. */ | ||
| 394 | void | ||
| 395 | term_fontpush(struct termp *p, enum termfont f) | ||
| 396 | { | ||
| 397 | |||
| 398 | 53450 | p->fontl = p->fontq[p->fonti]; | |
| 399 | ✗✓ | 26725 | 	if (++p->fonti == p->fontsz) { | 
| 400 | p->fontsz += 8; | ||
| 401 | p->fontq = mandoc_reallocarray(p->fontq, | ||
| 402 | p->fontsz, sizeof(*p->fontq)); | ||
| 403 | } | ||
| 404 | 26725 | p->fontq[p->fonti] = f; | |
| 405 | 26725 | } | |
| 406 | |||
| 407 | /* Flush to make the saved pointer current again. */ | ||
| 408 | void | ||
| 409 | term_fontpopq(struct termp *p, int i) | ||
| 410 | { | ||
| 411 | |||
| 412 | ✗✓ | 440512 | assert(i >= 0); | 
| 413 | ✓✓ | 220256 | if (p->fonti > i) | 
| 414 | 21361 | p->fonti = i; | |
| 415 | 220256 | } | |
| 416 | |||
| 417 | /* Pop one font off the stack. */ | ||
| 418 | void | ||
| 419 | term_fontpop(struct termp *p) | ||
| 420 | { | ||
| 421 | |||
| 422 | ✗✓ | 10728 | assert(p->fonti); | 
| 423 | 5364 | p->fonti--; | |
| 424 | 5364 | } | |
| 425 | |||
| 426 | /* | ||
| 427 | * Handle pwords, partial words, which may be either a single word or a | ||
| 428 | * phrase that cannot be broken down (such as a literal string). This | ||
| 429 | * handles word styling. | ||
| 430 | */ | ||
| 431 | void | ||
| 432 | term_word(struct termp *p, const char *word) | ||
| 433 | { | ||
| 434 | 508834 | struct roffsu su; | |
| 435 | 	const char	 nbrsp[2] = { ASCII_NBRSP, 0 }; | ||
| 436 | 508834 | const char *seq, *cp; | |
| 437 | 508834 | int sz, uc; | |
| 438 | 508834 | size_t csz, lsz, ssz; | |
| 439 | enum mandoc_esc esc; | ||
| 440 | |||
| 441 | ✓✗ | 508834 | 	if ((p->flags & TERMP_NOBUF) == 0) { | 
| 442 | ✓✓ | 508834 | 		if ((p->flags & TERMP_NOSPACE) == 0) { | 
| 443 | ✓✓ | 54974 | 			if ((p->flags & TERMP_KEEP) == 0) { | 
| 444 | 50530 | bufferc(p, ' '); | |
| 445 | ✓✓ | 50530 | if (p->flags & TERMP_SENTENCE) | 
| 446 | 1927 | bufferc(p, ' '); | |
| 447 | } else | ||
| 448 | 4444 | bufferc(p, ASCII_NBRSP); | |
| 449 | } | ||
| 450 | ✓✓ | 508834 | if (p->flags & TERMP_PREKEEP) | 
| 451 | 7521 | p->flags |= TERMP_KEEP; | |
| 452 | 1017668 | if (p->flags & TERMP_NONOSPACE) | |
| 453 | 508834 | p->flags |= TERMP_NOSPACE; | |
| 454 | else | ||
| 455 | 508834 | p->flags &= ~TERMP_NOSPACE; | |
| 456 | 508834 | p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); | |
| 457 | 508834 | p->skipvsp = 0; | |
| 458 | 508834 | } | |
| 459 | |||
| 460 | ✓✓ | 1094388 | 	while ('\0' != *word) { | 
| 461 | ✓✓ | 585554 | 		if ('\\' != *word) { | 
| 462 | ✓✓ | 522924 | 			if (TERMP_NBRWORD & p->flags) { | 
| 463 | ✓✓ | 1512 | 				if (' ' == *word) { | 
| 464 | 495 | encode(p, nbrsp, 1); | |
| 465 | 495 | word++; | |
| 466 | 495 | continue; | |
| 467 | } | ||
| 468 | 1017 | ssz = strcspn(word, "\\ "); | |
| 469 | 1017 | } else | |
| 470 | 521412 | ssz = strcspn(word, "\\"); | |
| 471 | 522429 | encode(p, word, ssz); | |
| 472 | 522429 | word += (int)ssz; | |
| 473 | 522429 | continue; | |
| 474 | } | ||
| 475 | |||
| 476 | 62630 | word++; | |
| 477 | 62630 | esc = mandoc_escape(&word, &seq, &sz); | |
| 478 | ✓✓ | 62630 | if (ESCAPE_ERROR == esc) | 
| 479 | continue; | ||
| 480 | |||
| 481 | ✓✓✓✓ ✓✓✓✗ ✓✓✓✓ ✓✓✓✓ | 1090066 | 		switch (esc) { | 
| 482 | case ESCAPE_UNICODE: | ||
| 483 | 6930 | uc = mchars_num2uc(seq + 1, sz - 1); | |
| 484 | 6930 | break; | |
| 485 | case ESCAPE_NUMBERED: | ||
| 486 | 729 | uc = mchars_num2char(seq, sz); | |
| 487 | ✓✓ | 729 | if (uc < 0) | 
| 488 | continue; | ||
| 489 | break; | ||
| 490 | case ESCAPE_SPECIAL: | ||
| 491 | ✓✓ | 28088 | 			if (p->enc == TERMENC_ASCII) { | 
| 492 | 24290 | cp = mchars_spec2str(seq, sz, &ssz); | |
| 493 | ✓✓ | 24290 | if (cp != NULL) | 
| 494 | 24146 | encode(p, cp, ssz); | |
| 495 | 			} else { | ||
| 496 | 3798 | uc = mchars_spec2cp(seq, sz); | |
| 497 | ✓✓ | 3798 | if (uc > 0) | 
| 498 | 3618 | encode1(p, uc); | |
| 499 | } | ||
| 500 | continue; | ||
| 501 | case ESCAPE_FONTBOLD: | ||
| 502 | 7074 | term_fontrepl(p, TERMFONT_BOLD); | |
| 503 | 7074 | continue; | |
| 504 | case ESCAPE_FONTITALIC: | ||
| 505 | 4923 | term_fontrepl(p, TERMFONT_UNDER); | |
| 506 | 4923 | continue; | |
| 507 | case ESCAPE_FONTBI: | ||
| 508 | 18 | term_fontrepl(p, TERMFONT_BI); | |
| 509 | 18 | continue; | |
| 510 | case ESCAPE_FONT: | ||
| 511 | case ESCAPE_FONTROMAN: | ||
| 512 | 13401 | term_fontrepl(p, TERMFONT_NONE); | |
| 513 | 13401 | continue; | |
| 514 | case ESCAPE_FONTPREV: | ||
| 515 | 927 | term_fontlast(p); | |
| 516 | 927 | continue; | |
| 517 | case ESCAPE_BREAK: | ||
| 518 | 36 | bufferc(p, '\n'); | |
| 519 | 36 | continue; | |
| 520 | case ESCAPE_NOSPACE: | ||
| 521 | ✓✓ | 63 | if (p->flags & TERMP_BACKAFTER) | 
| 522 | 9 | p->flags &= ~TERMP_BACKAFTER; | |
| 523 | ✗✓ | 54 | else if (*word == '\0') | 
| 524 | 54 | p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); | |
| 525 | continue; | ||
| 526 | case ESCAPE_HORIZ: | ||
| 527 | ✓✓ | 63 | 			if (*seq == '|') { | 
| 528 | 9 | seq++; | |
| 529 | 9 | uc = -p->col; | |
| 530 | 9 | } else | |
| 531 | uc = 0; | ||
| 532 | ✗✓ | 63 | if (a2roffsu(seq, &su, SCALE_EM) == NULL) | 
| 533 | continue; | ||
| 534 | 63 | uc += term_hen(p, &su); | |
| 535 | ✓✓ | 63 | if (uc > 0) | 
| 536 | ✓✓ | 72 | while (uc-- > 0) | 
| 537 | 27 | bufferc(p, ASCII_NBRSP); | |
| 538 | ✓✗ | 45 | else if (p->col > (size_t)(-uc)) | 
| 539 | 45 | p->col += uc; | |
| 540 | 			else { | ||
| 541 | uc += p->col; | ||
| 542 | p->col = 0; | ||
| 543 | 				if (p->tcol->offset > (size_t)(-uc)) { | ||
| 544 | p->ti += uc; | ||
| 545 | p->tcol->offset += uc; | ||
| 546 | 				} else { | ||
| 547 | p->ti -= p->tcol->offset; | ||
| 548 | p->tcol->offset = 0; | ||
| 549 | } | ||
| 550 | } | ||
| 551 | continue; | ||
| 552 | case ESCAPE_HLINE: | ||
| 553 | ✗✓ | 63 | if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL) | 
| 554 | continue; | ||
| 555 | 63 | uc = term_hen(p, &su); | |
| 556 | ✗✓ | 63 | 			if (uc <= 0) { | 
| 557 | if (p->tcol->rmargin <= p->tcol->offset) | ||
| 558 | continue; | ||
| 559 | lsz = p->tcol->rmargin - p->tcol->offset; | ||
| 560 | } else | ||
| 561 | 63 | lsz = uc; | |
| 562 | ✓✓ | 63 | if (*cp == seq[-1]) | 
| 563 | 18 | uc = -1; | |
| 564 | ✓✓ | 45 | 			else if (*cp == '\\') { | 
| 565 | 18 | seq = cp + 1; | |
| 566 | 18 | esc = mandoc_escape(&seq, &cp, &sz); | |
| 567 | ✗✗✓✗ | 18 | 				switch (esc) { | 
| 568 | case ESCAPE_UNICODE: | ||
| 569 | uc = mchars_num2uc(cp + 1, sz - 1); | ||
| 570 | break; | ||
| 571 | case ESCAPE_NUMBERED: | ||
| 572 | uc = mchars_num2char(cp, sz); | ||
| 573 | break; | ||
| 574 | case ESCAPE_SPECIAL: | ||
| 575 | 18 | uc = mchars_spec2cp(cp, sz); | |
| 576 | 18 | break; | |
| 577 | default: | ||
| 578 | uc = -1; | ||
| 579 | break; | ||
| 580 | } | ||
| 581 | } else | ||
| 582 | uc = *cp; | ||
| 583 | ✓✓✗✓ | 108 | if (uc < 0x20 || (uc > 0x7E && uc < 0xA0)) | 
| 584 | 18 | uc = '_'; | |
| 585 | ✓✗ | 63 | 			if (p->enc == TERMENC_ASCII) { | 
| 586 | 63 | cp = ascii_uc2str(uc); | |
| 587 | 63 | csz = term_strlen(p, cp); | |
| 588 | 63 | ssz = strlen(cp); | |
| 589 | 63 | } else | |
| 590 | csz = (*p->width)(p, uc); | ||
| 591 | ✓✓ | 549 | 			while (lsz >= csz) { | 
| 592 | ✓✗ | 243 | if (p->enc == TERMENC_ASCII) | 
| 593 | 243 | encode(p, cp, ssz); | |
| 594 | else | ||
| 595 | encode1(p, uc); | ||
| 596 | 243 | lsz -= csz; | |
| 597 | } | ||
| 598 | continue; | ||
| 599 | case ESCAPE_SKIPCHAR: | ||
| 600 | 81 | p->flags |= TERMP_BACKAFTER; | |
| 601 | 81 | continue; | |
| 602 | case ESCAPE_OVERSTRIKE: | ||
| 603 | 63 | cp = seq + sz; | |
| 604 | ✓✓ | 243 | 			while (seq < cp) { | 
| 605 | ✗✓ | 117 | 				if (*seq == '\\') { | 
| 606 | mandoc_escape(&seq, NULL, NULL); | ||
| 607 | continue; | ||
| 608 | } | ||
| 609 | 117 | encode1(p, *seq++); | |
| 610 | ✓✓ | 117 | 				if (seq < cp) { | 
| 611 | 63 | if (p->flags & TERMP_BACKBEFORE) | |
| 612 | p->flags |= TERMP_BACKAFTER; | ||
| 613 | else | ||
| 614 | p->flags |= TERMP_BACKBEFORE; | ||
| 615 | 63 | } | |
| 616 | } | ||
| 617 | /* Trim trailing backspace/blank pair. */ | ||
| 618 | ✓✗✗✓ | 126 | if (p->tcol->lastcol > 2 && | 
| 619 | ✓✗ | 63 | (p->tcol->buf[p->tcol->lastcol - 1] == ' ' || | 
| 620 | 63 | p->tcol->buf[p->tcol->lastcol - 1] == '\t')) | |
| 621 | p->tcol->lastcol -= 2; | ||
| 622 | ✓✗ | 63 | if (p->col > p->tcol->lastcol) | 
| 623 | p->col = p->tcol->lastcol; | ||
| 624 | continue; | ||
| 625 | default: | ||
| 626 | continue; | ||
| 627 | } | ||
| 628 | |||
| 629 | /* | ||
| 630 | * Common handling for Unicode and numbered | ||
| 631 | * character escape sequences. | ||
| 632 | */ | ||
| 633 | |||
| 634 | ✓✓ | 7623 | 		if (p->enc == TERMENC_ASCII) { | 
| 635 | 3762 | cp = ascii_uc2str(uc); | |
| 636 | 3762 | encode(p, cp, strlen(cp)); | |
| 637 | 3762 | 		} else { | |
| 638 | ✓✓ | 3861 | if ((uc < 0x20 && uc != 0x09) || | 
| 639 | ✓✓ | 3681 | (uc > 0x7E && uc < 0xA0)) | 
| 640 | 342 | uc = 0xFFFD; | |
| 641 | 3861 | encode1(p, uc); | |
| 642 | } | ||
| 643 | } | ||
| 644 | 508834 | p->flags &= ~TERMP_NBRWORD; | |
| 645 | 508834 | } | |
| 646 | |||
| 647 | static void | ||
| 648 | adjbuf(struct termp_col *c, size_t sz) | ||
| 649 | { | ||
| 650 | ✓✓ | 14520 | if (c->maxcols == 0) | 
| 651 | 7259 | c->maxcols = 1024; | |
| 652 | ✓✓ | 7262 | while (c->maxcols <= sz) | 
| 653 | 1 | c->maxcols <<= 2; | |
| 654 | 7260 | c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf)); | |
| 655 | 7260 | } | |
| 656 | |||
| 657 | static void | ||
| 658 | bufferc(struct termp *p, char c) | ||
| 659 | { | ||
| 660 | ✗✓ | 113928 | 	if (p->flags & TERMP_NOBUF) { | 
| 661 | (*p->letter)(p, c); | ||
| 662 | return; | ||
| 663 | } | ||
| 664 | ✗✓ | 56964 | if (p->col + 1 >= p->tcol->maxcols) | 
| 665 | adjbuf(p->tcol, p->col + 1); | ||
| 666 | ✗✓✗✗ ✗✗ | 56964 | if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) | 
| 667 | 56964 | p->tcol->buf[p->col] = c; | |
| 668 | ✓✗ | 56964 | if (p->tcol->lastcol < ++p->col) | 
| 669 | 56964 | p->tcol->lastcol = p->col; | |
| 670 | 56964 | } | |
| 671 | |||
| 672 | /* | ||
| 673 | * See encode(). | ||
| 674 | * Do this for a single (probably unicode) value. | ||
| 675 | * Does not check for non-decorated glyphs. | ||
| 676 | */ | ||
| 677 | static void | ||
| 678 | encode1(struct termp *p, int c) | ||
| 679 | { | ||
| 680 | enum termfont f; | ||
| 681 | |||
| 682 | ✗✓ | 4902128 | 	if (p->flags & TERMP_NOBUF) { | 
| 683 | (*p->letter)(p, c); | ||
| 684 | return; | ||
| 685 | } | ||
| 686 | |||
| 687 | ✓✓ | 2451064 | if (p->col + 7 >= p->tcol->maxcols) | 
| 688 | 9 | adjbuf(p->tcol, p->col + 7); | |
| 689 | |||
| 690 | ✓✓✓✓ | 9791836 | f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ? | 
| 691 | 2451046 | p->fontq[p->fonti] : TERMFONT_NONE; | |
| 692 | |||
| 693 | ✓✓ | 2451064 | 	if (p->flags & TERMP_BACKBEFORE) { | 
| 694 | ✓✗✗✓ | 234 | if (p->tcol->buf[p->col - 1] == ' ' || | 
| 695 | 117 | p->tcol->buf[p->col - 1] == '\t') | |
| 696 | p->col--; | ||
| 697 | else | ||
| 698 | 117 | p->tcol->buf[p->col++] = '\b'; | |
| 699 | 117 | p->flags &= ~TERMP_BACKBEFORE; | |
| 700 | 117 | } | |
| 701 | ✓✓ | 2451064 | 	if (f == TERMFONT_UNDER || f == TERMFONT_BI) { | 
| 702 | 72920 | p->tcol->buf[p->col++] = '_'; | |
| 703 | 72920 | p->tcol->buf[p->col++] = '\b'; | |
| 704 | 72920 | } | |
| 705 | ✓✓ | 2451064 | 	if (f == TERMFONT_BOLD || f == TERMFONT_BI) { | 
| 706 | ✓✓ | 215863 | if (c == ASCII_HYPH) | 
| 707 | 2232 | p->tcol->buf[p->col++] = '-'; | |
| 708 | else | ||
| 709 | 213631 | p->tcol->buf[p->col++] = c; | |
| 710 | 215863 | p->tcol->buf[p->col++] = '\b'; | |
| 711 | 215863 | } | |
| 712 | ✗✓✗✗ | 2451064 | if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) | 
| 713 | 2451064 | p->tcol->buf[p->col] = c; | |
| 714 | ✓✗ | 2451064 | if (p->tcol->lastcol < ++p->col) | 
| 715 | 2451064 | p->tcol->lastcol = p->col; | |
| 716 | ✓✓ | 2451064 | 	if (p->flags & TERMP_BACKAFTER) { | 
| 717 | 72 | p->flags |= TERMP_BACKBEFORE; | |
| 718 | 72 | p->flags &= ~TERMP_BACKAFTER; | |
| 719 | 72 | } | |
| 720 | 4902128 | } | |
| 721 | |||
| 722 | static void | ||
| 723 | encode(struct termp *p, const char *word, size_t sz) | ||
| 724 | { | ||
| 725 | size_t i; | ||
| 726 | |||
| 727 | ✗✓ | 1102150 | 	if (p->flags & TERMP_NOBUF) { | 
| 728 | for (i = 0; i < sz; i++) | ||
| 729 | (*p->letter)(p, word[i]); | ||
| 730 | return; | ||
| 731 | } | ||
| 732 | |||
| 733 | ✓✓ | 551075 | if (p->col + 2 + (sz * 5) >= p->tcol->maxcols) | 
| 734 | 7251 | adjbuf(p->tcol, p->col + 2 + (sz * 5)); | |
| 735 | |||
| 736 | ✓✓ | 6623958 | 	for (i = 0; i < sz; i++) { | 
| 737 | ✓✓✓✓ | 5516093 | if (ASCII_HYPH == word[i] || | 
| 738 | 2755189 | isgraph((unsigned char)word[i])) | |
| 739 | 2443468 | encode1(p, word[i]); | |
| 740 | 		else { | ||
| 741 | ✗✓✗✗ | 317436 | if (p->tcol->lastcol <= p->col || | 
| 742 | (word[i] != ' ' && word[i] != ASCII_NBRSP)) | ||
| 743 | 317436 | p->tcol->buf[p->col] = word[i]; | |
| 744 | 317436 | p->col++; | |
| 745 | |||
| 746 | /* | ||
| 747 | * Postpone the effect of \z while handling | ||
| 748 | * an overstrike sequence from ascii_uc2str(). | ||
| 749 | */ | ||
| 750 | |||
| 751 | ✓✓✗✓ | 319965 | if (word[i] == '\b' && | 
| 752 | 2529 | 			    (p->flags & TERMP_BACKBEFORE)) { | |
| 753 | p->flags &= ~TERMP_BACKBEFORE; | ||
| 754 | p->flags |= TERMP_BACKAFTER; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | } | ||
| 758 | ✓✓ | 551075 | if (p->tcol->lastcol < p->col) | 
| 759 | 24375 | p->tcol->lastcol = p->col; | |
| 760 | 1102150 | } | |
| 761 | |||
| 762 | void | ||
| 763 | term_setwidth(struct termp *p, const char *wstr) | ||
| 764 | { | ||
| 765 | 180 | struct roffsu su; | |
| 766 | int iop, width; | ||
| 767 | |||
| 768 | iop = 0; | ||
| 769 | width = 0; | ||
| 770 | ✓✓ | 90 | 	if (NULL != wstr) { | 
| 771 | ✓✗✓ | 90 | 		switch (*wstr) { | 
| 772 | case '+': | ||
| 773 | iop = 1; | ||
| 774 | 18 | wstr++; | |
| 775 | 18 | break; | |
| 776 | case '-': | ||
| 777 | iop = -1; | ||
| 778 | wstr++; | ||
| 779 | break; | ||
| 780 | default: | ||
| 781 | break; | ||
| 782 | } | ||
| 783 | ✓✓ | 72 | if (a2roffsu(wstr, &su, SCALE_MAX) != NULL) | 
| 784 | 54 | width = term_hspan(p, &su); | |
| 785 | else | ||
| 786 | iop = 0; | ||
| 787 | } | ||
| 788 | 90 | (*p->setwidth)(p, iop, width); | |
| 789 | 90 | } | |
| 790 | |||
| 791 | size_t | ||
| 792 | term_len(const struct termp *p, size_t sz) | ||
| 793 | { | ||
| 794 | |||
| 795 | 138078 | return (*p->width)(p, ' ') * sz; | |
| 796 | } | ||
| 797 | |||
| 798 | static size_t | ||
| 799 | cond_width(const struct termp *p, int c, int *skip) | ||
| 800 | { | ||
| 801 | |||
| 802 | ✗✓ | 1854396 | 	if (*skip) { | 
| 803 | (*skip) = 0; | ||
| 804 | return 0; | ||
| 805 | } else | ||
| 806 | 927198 | return (*p->width)(p, c); | |
| 807 | 927198 | } | |
| 808 | |||
| 809 | size_t | ||
| 810 | term_strlen(const struct termp *p, const char *cp) | ||
| 811 | { | ||
| 812 | 347450 | size_t sz, rsz, i; | |
| 813 | 347450 | int ssz, skip, uc; | |
| 814 | 347450 | const char *seq, *rhs; | |
| 815 | enum mandoc_esc esc; | ||
| 816 | 	static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH, | ||
| 817 | ASCII_BREAK, '\0' }; | ||
| 818 | |||
| 819 | /* | ||
| 820 | * Account for escaped sequences within string length | ||
| 821 | * calculations. This follows the logic in term_word() as we | ||
| 822 | * must calculate the width of produced strings. | ||
| 823 | */ | ||
| 824 | |||
| 825 | sz = 0; | ||
| 826 | 347450 | skip = 0; | |
| 827 | ✓✓ | 1043151 | 	while ('\0' != *cp) { | 
| 828 | 348251 | rsz = strcspn(cp, rej); | |
| 829 | ✓✓ | 2352250 | for (i = 0; i < rsz; i++) | 
| 830 | 827874 | sz += cond_width(p, *cp++, &skip); | |
| 831 | |||
| 832 | ✓✓✓✗ | 787325 | 		switch (*cp) { | 
| 833 | case '\\': | ||
| 834 | 1566 | cp++; | |
| 835 | 1566 | esc = mandoc_escape(&cp, &seq, &ssz); | |
| 836 | ✗✓ | 1566 | if (ESCAPE_ERROR == esc) | 
| 837 | continue; | ||
| 838 | |||
| 839 | rhs = NULL; | ||
| 840 | |||
| 841 | ✓✓✗✓ ✗✗ | 3726 | 			switch (esc) { | 
| 842 | case ESCAPE_UNICODE: | ||
| 843 | 1296 | uc = mchars_num2uc(seq + 1, ssz - 1); | |
| 844 | 1296 | break; | |
| 845 | case ESCAPE_NUMBERED: | ||
| 846 | uc = mchars_num2char(seq, ssz); | ||
| 847 | if (uc < 0) | ||
| 848 | continue; | ||
| 849 | break; | ||
| 850 | case ESCAPE_SPECIAL: | ||
| 851 | ✓✓ | 252 | 				if (p->enc == TERMENC_ASCII) { | 
| 852 | 126 | rhs = mchars_spec2str(seq, ssz, &rsz); | |
| 853 | ✓✗ | 126 | if (rhs != NULL) | 
| 854 | break; | ||
| 855 | 				} else { | ||
| 856 | 126 | uc = mchars_spec2cp(seq, ssz); | |
| 857 | ✓✗ | 126 | if (uc > 0) | 
| 858 | sz += cond_width(p, uc, &skip); | ||
| 859 | } | ||
| 860 | continue; | ||
| 861 | case ESCAPE_SKIPCHAR: | ||
| 862 | skip = 1; | ||
| 863 | continue; | ||
| 864 | case ESCAPE_OVERSTRIKE: | ||
| 865 | rsz = 0; | ||
| 866 | rhs = seq + ssz; | ||
| 867 | 				while (seq < rhs) { | ||
| 868 | 					if (*seq == '\\') { | ||
| 869 | mandoc_escape(&seq, NULL, NULL); | ||
| 870 | continue; | ||
| 871 | } | ||
| 872 | i = (*p->width)(p, *seq++); | ||
| 873 | if (rsz < i) | ||
| 874 | rsz = i; | ||
| 875 | } | ||
| 876 | sz += rsz; | ||
| 877 | continue; | ||
| 878 | default: | ||
| 879 | continue; | ||
| 880 | } | ||
| 881 | |||
| 882 | /* | ||
| 883 | * Common handling for Unicode and numbered | ||
| 884 | * character escape sequences. | ||
| 885 | */ | ||
| 886 | |||
| 887 | ✓✗ | 1296 | 			if (rhs == NULL) { | 
| 888 | ✓✓ | 1296 | 				if (p->enc == TERMENC_ASCII) { | 
| 889 | 648 | rhs = ascii_uc2str(uc); | |
| 890 | 648 | rsz = strlen(rhs); | |
| 891 | 				} else { | ||
| 892 | ✓✓ | 648 | if ((uc < 0x20 && uc != 0x09) || | 
| 893 | ✓✓ | 612 | (uc > 0x7E && uc < 0xA0)) | 
| 894 | 90 | uc = 0xFFFD; | |
| 895 | 648 | sz += cond_width(p, uc, &skip); | |
| 896 | 648 | continue; | |
| 897 | } | ||
| 898 | 648 | } | |
| 899 | |||
| 900 | ✗✓ | 648 | 			if (skip) { | 
| 901 | skip = 0; | ||
| 902 | break; | ||
| 903 | } | ||
| 904 | |||
| 905 | /* | ||
| 906 | * Common handling for all escape sequences | ||
| 907 | * printing more than one character. | ||
| 908 | */ | ||
| 909 | |||
| 910 | ✓✓ | 5436 | for (i = 0; i < rsz; i++) | 
| 911 | 2070 | sz += (*p->width)(p, *rhs++); | |
| 912 | break; | ||
| 913 | case ASCII_NBRSP: | ||
| 914 | 98676 | sz += cond_width(p, ' ', &skip); | |
| 915 | 98676 | cp++; | |
| 916 | 98676 | break; | |
| 917 | case ASCII_HYPH: | ||
| 918 | sz += cond_width(p, '-', &skip); | ||
| 919 | cp++; | ||
| 920 | break; | ||
| 921 | default: | ||
| 922 | break; | ||
| 923 | } | ||
| 924 | } | ||
| 925 | |||
| 926 | 347450 | return sz; | |
| 927 | 347450 | } | |
| 928 | |||
| 929 | int | ||
| 930 | term_vspan(const struct termp *p, const struct roffsu *su) | ||
| 931 | { | ||
| 932 | double r; | ||
| 933 | int ri; | ||
| 934 | |||
| 935 | ✓✓✗✓ ✗✓✓✗ ✓✓✗ | 6534 | 	switch (su->unit) { | 
| 936 | case SCALE_BU: | ||
| 937 | 36 | r = su->scale / 40.0; | |
| 938 | 36 | break; | |
| 939 | case SCALE_CM: | ||
| 940 | 27 | r = su->scale * 6.0 / 2.54; | |
| 941 | 27 | break; | |
| 942 | case SCALE_FS: | ||
| 943 | r = su->scale * 65536.0 / 40.0; | ||
| 944 | break; | ||
| 945 | case SCALE_IN: | ||
| 946 | 18 | r = su->scale * 6.0; | |
| 947 | 18 | break; | |
| 948 | case SCALE_MM: | ||
| 949 | r = su->scale * 0.006; | ||
| 950 | break; | ||
| 951 | case SCALE_PC: | ||
| 952 | 36 | r = su->scale; | |
| 953 | 36 | break; | |
| 954 | case SCALE_PT: | ||
| 955 | 45 | r = su->scale / 12.0; | |
| 956 | 45 | break; | |
| 957 | case SCALE_EN: | ||
| 958 | case SCALE_EM: | ||
| 959 | 54 | r = su->scale * 0.6; | |
| 960 | 54 | break; | |
| 961 | case SCALE_VS: | ||
| 962 | 3051 | r = su->scale; | |
| 963 | 3051 | break; | |
| 964 | default: | ||
| 965 | abort(); | ||
| 966 | } | ||
| 967 | 3267 | ri = r > 0.0 ? r + 0.4995 : r - 0.4995; | |
| 968 | 3267 | return ri < 66 ? ri : 1; | |
| 969 | } | ||
| 970 | |||
| 971 | /* | ||
| 972 | * Convert a scaling width to basic units, rounding towards 0. | ||
| 973 | */ | ||
| 974 | int | ||
| 975 | term_hspan(const struct termp *p, const struct roffsu *su) | ||
| 976 | { | ||
| 977 | |||
| 978 | 108 | return (*p->hspan)(p, su); | |
| 979 | } | ||
| 980 | |||
| 981 | /* | ||
| 982 | * Convert a scaling width to basic units, rounding to closest. | ||
| 983 | */ | ||
| 984 | int | ||
| 985 | term_hen(const struct termp *p, const struct roffsu *su) | ||
| 986 | { | ||
| 987 | int bu; | ||
| 988 | |||
| 989 | ✓✓ | 71916 | if ((bu = (*p->hspan)(p, su)) >= 0) | 
| 990 | 35175 | return (bu + 11) / 24; | |
| 991 | else | ||
| 992 | 783 | return -((-bu + 11) / 24); | |
| 993 | 35958 | } | 
| Generated by: GCOVR (Version 3.3) |