1241675Suqs/* $Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#ifdef HAVE_CONFIG_H 19241675Suqs#include "config.h" 20241675Suqs#endif 21241675Suqs 22241675Suqs#include <sys/types.h> 23241675Suqs 24241675Suqs#include <assert.h> 25241675Suqs#include <ctype.h> 26241675Suqs#include <stdint.h> 27241675Suqs#include <stdio.h> 28241675Suqs#include <stdlib.h> 29241675Suqs#include <string.h> 30241675Suqs 31241675Suqs#include "mandoc.h" 32241675Suqs#include "out.h" 33241675Suqs#include "term.h" 34241675Suqs#include "main.h" 35241675Suqs 36241675Suqsstatic void adjbuf(struct termp *p, int); 37241675Suqsstatic void bufferc(struct termp *, char); 38241675Suqsstatic void encode(struct termp *, const char *, size_t); 39241675Suqsstatic void encode1(struct termp *, int); 40241675Suqs 41241675Suqsvoid 42241675Suqsterm_free(struct termp *p) 43241675Suqs{ 44241675Suqs 45241675Suqs if (p->buf) 46241675Suqs free(p->buf); 47241675Suqs if (p->symtab) 48241675Suqs mchars_free(p->symtab); 49241675Suqs 50241675Suqs free(p); 51241675Suqs} 52241675Suqs 53241675Suqs 54241675Suqsvoid 55241675Suqsterm_begin(struct termp *p, term_margin head, 56241675Suqs term_margin foot, const void *arg) 57241675Suqs{ 58241675Suqs 59241675Suqs p->headf = head; 60241675Suqs p->footf = foot; 61241675Suqs p->argf = arg; 62241675Suqs (*p->begin)(p); 63241675Suqs} 64241675Suqs 65241675Suqs 66241675Suqsvoid 67241675Suqsterm_end(struct termp *p) 68241675Suqs{ 69241675Suqs 70241675Suqs (*p->end)(p); 71241675Suqs} 72241675Suqs 73241675Suqs/* 74241675Suqs * Flush a line of text. A "line" is loosely defined as being something 75241675Suqs * that should be followed by a newline, regardless of whether it's 76241675Suqs * broken apart by newlines getting there. A line can also be a 77241675Suqs * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does 78241675Suqs * not have a trailing newline. 79241675Suqs * 80241675Suqs * The following flags may be specified: 81241675Suqs * 82241675Suqs * - TERMP_NOBREAK: this is the most important and is used when making 83241675Suqs * columns. In short: don't print a newline and instead expect the 84241675Suqs * next call to do the padding up to the start of the next column. 85241675Suqs * 86241675Suqs * - TERMP_TWOSPACE: make sure there is room for at least two space 87241675Suqs * characters of padding. Otherwise, rather break the line. 88241675Suqs * 89241675Suqs * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and 90241675Suqs * the line is overrun, and don't pad-right if it's underrun. 91241675Suqs * 92241675Suqs * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when 93241675Suqs * overrunning, instead save the position and continue at that point 94241675Suqs * when the next invocation. 95241675Suqs * 96241675Suqs * In-line line breaking: 97241675Suqs * 98241675Suqs * If TERMP_NOBREAK is specified and the line overruns the right 99241675Suqs * margin, it will break and pad-right to the right margin after 100241675Suqs * writing. If maxrmargin is violated, it will break and continue 101241675Suqs * writing from the right-margin, which will lead to the above scenario 102241675Suqs * upon exit. Otherwise, the line will break at the right margin. 103241675Suqs */ 104241675Suqsvoid 105241675Suqsterm_flushln(struct termp *p) 106241675Suqs{ 107241675Suqs int i; /* current input position in p->buf */ 108241675Suqs size_t vis; /* current visual position on output */ 109241675Suqs size_t vbl; /* number of blanks to prepend to output */ 110241675Suqs size_t vend; /* end of word visual position on output */ 111241675Suqs size_t bp; /* visual right border position */ 112241675Suqs size_t dv; /* temporary for visual pos calculations */ 113241675Suqs int j; /* temporary loop index for p->buf */ 114241675Suqs int jhy; /* last hyph before overflow w/r/t j */ 115241675Suqs size_t maxvis; /* output position of visible boundary */ 116241675Suqs size_t mmax; /* used in calculating bp */ 117241675Suqs 118241675Suqs /* 119241675Suqs * First, establish the maximum columns of "visible" content. 120241675Suqs * This is usually the difference between the right-margin and 121241675Suqs * an indentation, but can be, for tagged lists or columns, a 122241675Suqs * small set of values. 123241675Suqs */ 124241675Suqs assert (p->rmargin >= p->offset); 125241675Suqs dv = p->rmargin - p->offset; 126241675Suqs maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 127241675Suqs dv = p->maxrmargin - p->offset; 128241675Suqs mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 129241675Suqs 130241675Suqs bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; 131241675Suqs 132241675Suqs /* 133241675Suqs * Calculate the required amount of padding. 134241675Suqs */ 135241675Suqs vbl = p->offset + p->overstep > p->viscol ? 136241675Suqs p->offset + p->overstep - p->viscol : 0; 137241675Suqs 138241675Suqs vis = vend = 0; 139241675Suqs i = 0; 140241675Suqs 141241675Suqs while (i < p->col) { 142241675Suqs /* 143241675Suqs * Handle literal tab characters: collapse all 144241675Suqs * subsequent tabs into a single huge set of spaces. 145241675Suqs */ 146241675Suqs while (i < p->col && '\t' == p->buf[i]) { 147241675Suqs vend = (vis / p->tabwidth + 1) * p->tabwidth; 148241675Suqs vbl += vend - vis; 149241675Suqs vis = vend; 150241675Suqs i++; 151241675Suqs } 152241675Suqs 153241675Suqs /* 154241675Suqs * Count up visible word characters. Control sequences 155241675Suqs * (starting with the CSI) aren't counted. A space 156241675Suqs * generates a non-printing word, which is valid (the 157241675Suqs * space is printed according to regular spacing rules). 158241675Suqs */ 159241675Suqs 160241675Suqs for (j = i, jhy = 0; j < p->col; j++) { 161241675Suqs if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j]) 162241675Suqs break; 163241675Suqs 164241675Suqs /* Back over the the last printed character. */ 165241675Suqs if (8 == p->buf[j]) { 166241675Suqs assert(j); 167241675Suqs vend -= (*p->width)(p, p->buf[j - 1]); 168241675Suqs continue; 169241675Suqs } 170241675Suqs 171241675Suqs /* Regular word. */ 172241675Suqs /* Break at the hyphen point if we overrun. */ 173241675Suqs if (vend > vis && vend < bp && 174241675Suqs ASCII_HYPH == p->buf[j]) 175241675Suqs jhy = j; 176241675Suqs 177241675Suqs vend += (*p->width)(p, p->buf[j]); 178241675Suqs } 179241675Suqs 180241675Suqs /* 181241675Suqs * Find out whether we would exceed the right margin. 182241675Suqs * If so, break to the next line. 183241675Suqs */ 184241675Suqs if (vend > bp && 0 == jhy && vis > 0) { 185241675Suqs vend -= vis; 186241675Suqs (*p->endline)(p); 187241675Suqs p->viscol = 0; 188241675Suqs if (TERMP_NOBREAK & p->flags) { 189241675Suqs vbl = p->rmargin; 190241675Suqs vend += p->rmargin - p->offset; 191241675Suqs } else 192241675Suqs vbl = p->offset; 193241675Suqs 194241675Suqs /* Remove the p->overstep width. */ 195241675Suqs 196241675Suqs bp += (size_t)p->overstep; 197241675Suqs p->overstep = 0; 198241675Suqs } 199241675Suqs 200241675Suqs /* Write out the [remaining] word. */ 201241675Suqs for ( ; i < p->col; i++) { 202241675Suqs if (vend > bp && jhy > 0 && i > jhy) 203241675Suqs break; 204241675Suqs if ('\t' == p->buf[i]) 205241675Suqs break; 206241675Suqs if (' ' == p->buf[i]) { 207241675Suqs j = i; 208241675Suqs while (' ' == p->buf[i]) 209241675Suqs i++; 210241675Suqs dv = (size_t)(i - j) * (*p->width)(p, ' '); 211241675Suqs vbl += dv; 212241675Suqs vend += dv; 213241675Suqs break; 214241675Suqs } 215241675Suqs if (ASCII_NBRSP == p->buf[i]) { 216241675Suqs vbl += (*p->width)(p, ' '); 217241675Suqs continue; 218241675Suqs } 219241675Suqs 220241675Suqs /* 221241675Suqs * Now we definitely know there will be 222241675Suqs * printable characters to output, 223241675Suqs * so write preceding white space now. 224241675Suqs */ 225241675Suqs if (vbl) { 226241675Suqs (*p->advance)(p, vbl); 227241675Suqs p->viscol += vbl; 228241675Suqs vbl = 0; 229241675Suqs } 230241675Suqs 231241675Suqs if (ASCII_HYPH == p->buf[i]) { 232241675Suqs (*p->letter)(p, '-'); 233241675Suqs p->viscol += (*p->width)(p, '-'); 234241675Suqs continue; 235241675Suqs } 236241675Suqs 237241675Suqs (*p->letter)(p, p->buf[i]); 238241675Suqs if (8 == p->buf[i]) 239241675Suqs p->viscol -= (*p->width)(p, p->buf[i-1]); 240241675Suqs else 241241675Suqs p->viscol += (*p->width)(p, p->buf[i]); 242241675Suqs } 243241675Suqs vis = vend; 244241675Suqs } 245241675Suqs 246241675Suqs /* 247241675Suqs * If there was trailing white space, it was not printed; 248241675Suqs * so reset the cursor position accordingly. 249241675Suqs */ 250241675Suqs if (vis) 251241675Suqs vis -= vbl; 252241675Suqs 253241675Suqs p->col = 0; 254241675Suqs p->overstep = 0; 255241675Suqs 256241675Suqs if ( ! (TERMP_NOBREAK & p->flags)) { 257241675Suqs p->viscol = 0; 258241675Suqs (*p->endline)(p); 259241675Suqs return; 260241675Suqs } 261241675Suqs 262241675Suqs if (TERMP_HANG & p->flags) { 263241675Suqs /* We need one blank after the tag. */ 264241675Suqs p->overstep = (int)(vis - maxvis + (*p->width)(p, ' ')); 265241675Suqs 266241675Suqs /* 267241675Suqs * Behave exactly the same way as groff: 268241675Suqs * If we have overstepped the margin, temporarily move 269241675Suqs * it to the right and flag the rest of the line to be 270241675Suqs * shorter. 271241675Suqs * If we landed right at the margin, be happy. 272241675Suqs * If we are one step before the margin, temporarily 273241675Suqs * move it one step LEFT and flag the rest of the line 274241675Suqs * to be longer. 275241675Suqs */ 276241675Suqs if (p->overstep < -1) 277241675Suqs p->overstep = 0; 278241675Suqs return; 279241675Suqs 280241675Suqs } else if (TERMP_DANGLE & p->flags) 281241675Suqs return; 282241675Suqs 283241675Suqs /* If the column was overrun, break the line. */ 284241675Suqs if (maxvis <= vis + 285241675Suqs ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) { 286241675Suqs (*p->endline)(p); 287241675Suqs p->viscol = 0; 288241675Suqs } 289241675Suqs} 290241675Suqs 291241675Suqs 292241675Suqs/* 293241675Suqs * A newline only breaks an existing line; it won't assert vertical 294241675Suqs * space. All data in the output buffer is flushed prior to the newline 295241675Suqs * assertion. 296241675Suqs */ 297241675Suqsvoid 298241675Suqsterm_newln(struct termp *p) 299241675Suqs{ 300241675Suqs 301241675Suqs p->flags |= TERMP_NOSPACE; 302241675Suqs if (p->col || p->viscol) 303241675Suqs term_flushln(p); 304241675Suqs} 305241675Suqs 306241675Suqs 307241675Suqs/* 308241675Suqs * Asserts a vertical space (a full, empty line-break between lines). 309241675Suqs * Note that if used twice, this will cause two blank spaces and so on. 310241675Suqs * All data in the output buffer is flushed prior to the newline 311241675Suqs * assertion. 312241675Suqs */ 313241675Suqsvoid 314241675Suqsterm_vspace(struct termp *p) 315241675Suqs{ 316241675Suqs 317241675Suqs term_newln(p); 318241675Suqs p->viscol = 0; 319241675Suqs (*p->endline)(p); 320241675Suqs} 321241675Suqs 322241675Suqsvoid 323241675Suqsterm_fontlast(struct termp *p) 324241675Suqs{ 325241675Suqs enum termfont f; 326241675Suqs 327241675Suqs f = p->fontl; 328241675Suqs p->fontl = p->fontq[p->fonti]; 329241675Suqs p->fontq[p->fonti] = f; 330241675Suqs} 331241675Suqs 332241675Suqs 333241675Suqsvoid 334241675Suqsterm_fontrepl(struct termp *p, enum termfont f) 335241675Suqs{ 336241675Suqs 337241675Suqs p->fontl = p->fontq[p->fonti]; 338241675Suqs p->fontq[p->fonti] = f; 339241675Suqs} 340241675Suqs 341241675Suqs 342241675Suqsvoid 343241675Suqsterm_fontpush(struct termp *p, enum termfont f) 344241675Suqs{ 345241675Suqs 346241675Suqs assert(p->fonti + 1 < 10); 347241675Suqs p->fontl = p->fontq[p->fonti]; 348241675Suqs p->fontq[++p->fonti] = f; 349241675Suqs} 350241675Suqs 351241675Suqs 352241675Suqsconst void * 353241675Suqsterm_fontq(struct termp *p) 354241675Suqs{ 355241675Suqs 356241675Suqs return(&p->fontq[p->fonti]); 357241675Suqs} 358241675Suqs 359241675Suqs 360241675Suqsenum termfont 361241675Suqsterm_fonttop(struct termp *p) 362241675Suqs{ 363241675Suqs 364241675Suqs return(p->fontq[p->fonti]); 365241675Suqs} 366241675Suqs 367241675Suqs 368241675Suqsvoid 369241675Suqsterm_fontpopq(struct termp *p, const void *key) 370241675Suqs{ 371241675Suqs 372241675Suqs while (p->fonti >= 0 && key != &p->fontq[p->fonti]) 373241675Suqs p->fonti--; 374241675Suqs assert(p->fonti >= 0); 375241675Suqs} 376241675Suqs 377241675Suqs 378241675Suqsvoid 379241675Suqsterm_fontpop(struct termp *p) 380241675Suqs{ 381241675Suqs 382241675Suqs assert(p->fonti); 383241675Suqs p->fonti--; 384241675Suqs} 385241675Suqs 386241675Suqs/* 387241675Suqs * Handle pwords, partial words, which may be either a single word or a 388241675Suqs * phrase that cannot be broken down (such as a literal string). This 389241675Suqs * handles word styling. 390241675Suqs */ 391241675Suqsvoid 392241675Suqsterm_word(struct termp *p, const char *word) 393241675Suqs{ 394241675Suqs const char *seq, *cp; 395241675Suqs char c; 396241675Suqs int sz, uc; 397241675Suqs size_t ssz; 398241675Suqs enum mandoc_esc esc; 399241675Suqs 400241675Suqs if ( ! (TERMP_NOSPACE & p->flags)) { 401241675Suqs if ( ! (TERMP_KEEP & p->flags)) { 402241675Suqs if (TERMP_PREKEEP & p->flags) 403241675Suqs p->flags |= TERMP_KEEP; 404241675Suqs bufferc(p, ' '); 405241675Suqs if (TERMP_SENTENCE & p->flags) 406241675Suqs bufferc(p, ' '); 407241675Suqs } else 408241675Suqs bufferc(p, ASCII_NBRSP); 409241675Suqs } 410241675Suqs 411241675Suqs if ( ! (p->flags & TERMP_NONOSPACE)) 412241675Suqs p->flags &= ~TERMP_NOSPACE; 413241675Suqs else 414241675Suqs p->flags |= TERMP_NOSPACE; 415241675Suqs 416241675Suqs p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM); 417241675Suqs 418241675Suqs while ('\0' != *word) { 419241675Suqs if ((ssz = strcspn(word, "\\")) > 0) 420241675Suqs encode(p, word, ssz); 421241675Suqs 422241675Suqs word += (int)ssz; 423241675Suqs if ('\\' != *word) 424241675Suqs continue; 425241675Suqs 426241675Suqs word++; 427241675Suqs esc = mandoc_escape(&word, &seq, &sz); 428241675Suqs if (ESCAPE_ERROR == esc) 429241675Suqs break; 430241675Suqs 431241675Suqs if (TERMENC_ASCII != p->enc) 432241675Suqs switch (esc) { 433241675Suqs case (ESCAPE_UNICODE): 434241675Suqs uc = mchars_num2uc(seq + 1, sz - 1); 435241675Suqs if ('\0' == uc) 436241675Suqs break; 437241675Suqs encode1(p, uc); 438241675Suqs continue; 439241675Suqs case (ESCAPE_SPECIAL): 440241675Suqs uc = mchars_spec2cp(p->symtab, seq, sz); 441241675Suqs if (uc <= 0) 442241675Suqs break; 443241675Suqs encode1(p, uc); 444241675Suqs continue; 445241675Suqs default: 446241675Suqs break; 447241675Suqs } 448241675Suqs 449241675Suqs switch (esc) { 450241675Suqs case (ESCAPE_UNICODE): 451241675Suqs encode1(p, '?'); 452241675Suqs break; 453241675Suqs case (ESCAPE_NUMBERED): 454241675Suqs c = mchars_num2char(seq, sz); 455241675Suqs if ('\0' != c) 456241675Suqs encode(p, &c, 1); 457241675Suqs break; 458241675Suqs case (ESCAPE_SPECIAL): 459241675Suqs cp = mchars_spec2str(p->symtab, seq, sz, &ssz); 460241675Suqs if (NULL != cp) 461241675Suqs encode(p, cp, ssz); 462241675Suqs else if (1 == ssz) 463241675Suqs encode(p, seq, sz); 464241675Suqs break; 465241675Suqs case (ESCAPE_FONTBOLD): 466241675Suqs term_fontrepl(p, TERMFONT_BOLD); 467241675Suqs break; 468241675Suqs case (ESCAPE_FONTITALIC): 469241675Suqs term_fontrepl(p, TERMFONT_UNDER); 470241675Suqs break; 471241675Suqs case (ESCAPE_FONT): 472241675Suqs /* FALLTHROUGH */ 473241675Suqs case (ESCAPE_FONTROMAN): 474241675Suqs term_fontrepl(p, TERMFONT_NONE); 475241675Suqs break; 476241675Suqs case (ESCAPE_FONTPREV): 477241675Suqs term_fontlast(p); 478241675Suqs break; 479241675Suqs case (ESCAPE_NOSPACE): 480241675Suqs if ('\0' == *word) 481241675Suqs p->flags |= TERMP_NOSPACE; 482241675Suqs break; 483241675Suqs default: 484241675Suqs break; 485241675Suqs } 486241675Suqs } 487241675Suqs} 488241675Suqs 489241675Suqsstatic void 490241675Suqsadjbuf(struct termp *p, int sz) 491241675Suqs{ 492241675Suqs 493241675Suqs if (0 == p->maxcols) 494241675Suqs p->maxcols = 1024; 495241675Suqs while (sz >= p->maxcols) 496241675Suqs p->maxcols <<= 2; 497241675Suqs 498241675Suqs p->buf = mandoc_realloc 499241675Suqs (p->buf, sizeof(int) * (size_t)p->maxcols); 500241675Suqs} 501241675Suqs 502241675Suqsstatic void 503241675Suqsbufferc(struct termp *p, char c) 504241675Suqs{ 505241675Suqs 506241675Suqs if (p->col + 1 >= p->maxcols) 507241675Suqs adjbuf(p, p->col + 1); 508241675Suqs 509241675Suqs p->buf[p->col++] = c; 510241675Suqs} 511241675Suqs 512241675Suqs/* 513241675Suqs * See encode(). 514241675Suqs * Do this for a single (probably unicode) value. 515241675Suqs * Does not check for non-decorated glyphs. 516241675Suqs */ 517241675Suqsstatic void 518241675Suqsencode1(struct termp *p, int c) 519241675Suqs{ 520241675Suqs enum termfont f; 521241675Suqs 522241675Suqs if (p->col + 4 >= p->maxcols) 523241675Suqs adjbuf(p, p->col + 4); 524241675Suqs 525241675Suqs f = term_fonttop(p); 526241675Suqs 527241675Suqs if (TERMFONT_NONE == f) { 528241675Suqs p->buf[p->col++] = c; 529241675Suqs return; 530241675Suqs } else if (TERMFONT_UNDER == f) { 531241675Suqs p->buf[p->col++] = '_'; 532241675Suqs } else 533241675Suqs p->buf[p->col++] = c; 534241675Suqs 535241675Suqs p->buf[p->col++] = 8; 536241675Suqs p->buf[p->col++] = c; 537241675Suqs} 538241675Suqs 539241675Suqsstatic void 540241675Suqsencode(struct termp *p, const char *word, size_t sz) 541241675Suqs{ 542241675Suqs enum termfont f; 543241675Suqs int i, len; 544241675Suqs 545241675Suqs /* LINTED */ 546241675Suqs len = sz; 547241675Suqs 548241675Suqs /* 549241675Suqs * Encode and buffer a string of characters. If the current 550241675Suqs * font mode is unset, buffer directly, else encode then buffer 551241675Suqs * character by character. 552241675Suqs */ 553241675Suqs 554241675Suqs if (TERMFONT_NONE == (f = term_fonttop(p))) { 555241675Suqs if (p->col + len >= p->maxcols) 556241675Suqs adjbuf(p, p->col + len); 557241675Suqs for (i = 0; i < len; i++) 558241675Suqs p->buf[p->col++] = word[i]; 559241675Suqs return; 560241675Suqs } 561241675Suqs 562241675Suqs /* Pre-buffer, assuming worst-case. */ 563241675Suqs 564241675Suqs if (p->col + 1 + (len * 3) >= p->maxcols) 565241675Suqs adjbuf(p, p->col + 1 + (len * 3)); 566241675Suqs 567241675Suqs for (i = 0; i < len; i++) { 568241675Suqs if (ASCII_HYPH != word[i] && 569241675Suqs ! isgraph((unsigned char)word[i])) { 570241675Suqs p->buf[p->col++] = word[i]; 571241675Suqs continue; 572241675Suqs } 573241675Suqs 574241675Suqs if (TERMFONT_UNDER == f) 575241675Suqs p->buf[p->col++] = '_'; 576241675Suqs else if (ASCII_HYPH == word[i]) 577241675Suqs p->buf[p->col++] = '-'; 578241675Suqs else 579241675Suqs p->buf[p->col++] = word[i]; 580241675Suqs 581241675Suqs p->buf[p->col++] = 8; 582241675Suqs p->buf[p->col++] = word[i]; 583241675Suqs } 584241675Suqs} 585241675Suqs 586241675Suqssize_t 587241675Suqsterm_len(const struct termp *p, size_t sz) 588241675Suqs{ 589241675Suqs 590241675Suqs return((*p->width)(p, ' ') * sz); 591241675Suqs} 592241675Suqs 593241675Suqs 594241675Suqssize_t 595241675Suqsterm_strlen(const struct termp *p, const char *cp) 596241675Suqs{ 597241675Suqs size_t sz, rsz, i; 598241675Suqs int ssz, c; 599241675Suqs const char *seq, *rhs; 600241675Suqs enum mandoc_esc esc; 601241675Suqs static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' }; 602241675Suqs 603241675Suqs /* 604241675Suqs * Account for escaped sequences within string length 605241675Suqs * calculations. This follows the logic in term_word() as we 606241675Suqs * must calculate the width of produced strings. 607241675Suqs */ 608241675Suqs 609241675Suqs sz = 0; 610241675Suqs while ('\0' != *cp) { 611241675Suqs rsz = strcspn(cp, rej); 612241675Suqs for (i = 0; i < rsz; i++) 613241675Suqs sz += (*p->width)(p, *cp++); 614241675Suqs 615241675Suqs c = 0; 616241675Suqs switch (*cp) { 617241675Suqs case ('\\'): 618241675Suqs cp++; 619241675Suqs esc = mandoc_escape(&cp, &seq, &ssz); 620241675Suqs if (ESCAPE_ERROR == esc) 621241675Suqs return(sz); 622241675Suqs 623241675Suqs if (TERMENC_ASCII != p->enc) 624241675Suqs switch (esc) { 625241675Suqs case (ESCAPE_UNICODE): 626241675Suqs c = mchars_num2uc 627241675Suqs (seq + 1, ssz - 1); 628241675Suqs if ('\0' == c) 629241675Suqs break; 630241675Suqs sz += (*p->width)(p, c); 631241675Suqs continue; 632241675Suqs case (ESCAPE_SPECIAL): 633241675Suqs c = mchars_spec2cp 634241675Suqs (p->symtab, seq, ssz); 635241675Suqs if (c <= 0) 636241675Suqs break; 637241675Suqs sz += (*p->width)(p, c); 638241675Suqs continue; 639241675Suqs default: 640241675Suqs break; 641241675Suqs } 642241675Suqs 643241675Suqs rhs = NULL; 644241675Suqs 645241675Suqs switch (esc) { 646241675Suqs case (ESCAPE_UNICODE): 647241675Suqs sz += (*p->width)(p, '?'); 648241675Suqs break; 649241675Suqs case (ESCAPE_NUMBERED): 650241675Suqs c = mchars_num2char(seq, ssz); 651241675Suqs if ('\0' != c) 652241675Suqs sz += (*p->width)(p, c); 653241675Suqs break; 654241675Suqs case (ESCAPE_SPECIAL): 655241675Suqs rhs = mchars_spec2str 656241675Suqs (p->symtab, seq, ssz, &rsz); 657241675Suqs 658241675Suqs if (ssz != 1 || rhs) 659241675Suqs break; 660241675Suqs 661241675Suqs rhs = seq; 662241675Suqs rsz = ssz; 663241675Suqs break; 664241675Suqs default: 665241675Suqs break; 666241675Suqs } 667241675Suqs 668241675Suqs if (NULL == rhs) 669241675Suqs break; 670241675Suqs 671241675Suqs for (i = 0; i < rsz; i++) 672241675Suqs sz += (*p->width)(p, *rhs++); 673241675Suqs break; 674241675Suqs case (ASCII_NBRSP): 675241675Suqs sz += (*p->width)(p, ' '); 676241675Suqs cp++; 677241675Suqs break; 678241675Suqs case (ASCII_HYPH): 679241675Suqs sz += (*p->width)(p, '-'); 680241675Suqs cp++; 681241675Suqs break; 682241675Suqs default: 683241675Suqs break; 684241675Suqs } 685241675Suqs } 686241675Suqs 687241675Suqs return(sz); 688241675Suqs} 689241675Suqs 690241675Suqs/* ARGSUSED */ 691241675Suqssize_t 692241675Suqsterm_vspan(const struct termp *p, const struct roffsu *su) 693241675Suqs{ 694241675Suqs double r; 695241675Suqs 696241675Suqs switch (su->unit) { 697241675Suqs case (SCALE_CM): 698241675Suqs r = su->scale * 2; 699241675Suqs break; 700241675Suqs case (SCALE_IN): 701241675Suqs r = su->scale * 6; 702241675Suqs break; 703241675Suqs case (SCALE_PC): 704241675Suqs r = su->scale; 705241675Suqs break; 706241675Suqs case (SCALE_PT): 707241675Suqs r = su->scale / 8; 708241675Suqs break; 709241675Suqs case (SCALE_MM): 710241675Suqs r = su->scale / 1000; 711241675Suqs break; 712241675Suqs case (SCALE_VS): 713241675Suqs r = su->scale; 714241675Suqs break; 715241675Suqs default: 716241675Suqs r = su->scale - 1; 717241675Suqs break; 718241675Suqs } 719241675Suqs 720241675Suqs if (r < 0.0) 721241675Suqs r = 0.0; 722241675Suqs return(/* LINTED */(size_t) 723241675Suqs r); 724241675Suqs} 725241675Suqs 726241675Suqssize_t 727241675Suqsterm_hspan(const struct termp *p, const struct roffsu *su) 728241675Suqs{ 729241675Suqs double v; 730241675Suqs 731241675Suqs v = ((*p->hspan)(p, su)); 732241675Suqs if (v < 0.0) 733241675Suqs v = 0.0; 734241675Suqs return((size_t) /* LINTED */ 735241675Suqs v); 736241675Suqs} 737