1*260e9a87SYuri Pankov /* $Id: term.c,v 1.245 2015/03/06 13:02:43 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 395c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*260e9a87SYuri Pankov * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org> 595c635efSGarrett D'Amore * 695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 995c635efSGarrett D'Amore * 1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1795c635efSGarrett D'Amore */ 1895c635efSGarrett D'Amore #include "config.h" 1995c635efSGarrett D'Amore 2095c635efSGarrett D'Amore #include <sys/types.h> 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <assert.h> 2395c635efSGarrett D'Amore #include <ctype.h> 2495c635efSGarrett D'Amore #include <stdio.h> 2595c635efSGarrett D'Amore #include <stdlib.h> 2695c635efSGarrett D'Amore #include <string.h> 2795c635efSGarrett D'Amore 2895c635efSGarrett D'Amore #include "mandoc.h" 29*260e9a87SYuri Pankov #include "mandoc_aux.h" 3095c635efSGarrett D'Amore #include "out.h" 3195c635efSGarrett D'Amore #include "term.h" 3295c635efSGarrett D'Amore #include "main.h" 3395c635efSGarrett D'Amore 34698f87a4SGarrett D'Amore static size_t cond_width(const struct termp *, int, int *); 35698f87a4SGarrett D'Amore static void adjbuf(struct termp *p, size_t); 3695c635efSGarrett D'Amore static void bufferc(struct termp *, char); 3795c635efSGarrett D'Amore static void encode(struct termp *, const char *, size_t); 3895c635efSGarrett D'Amore static void encode1(struct termp *, int); 3995c635efSGarrett D'Amore 40*260e9a87SYuri Pankov 4195c635efSGarrett D'Amore void 4295c635efSGarrett D'Amore term_free(struct termp *p) 4395c635efSGarrett D'Amore { 4495c635efSGarrett D'Amore 4595c635efSGarrett D'Amore free(p->buf); 46*260e9a87SYuri Pankov free(p->fontq); 4795c635efSGarrett D'Amore free(p); 4895c635efSGarrett D'Amore } 4995c635efSGarrett D'Amore 5095c635efSGarrett D'Amore void 5195c635efSGarrett D'Amore term_begin(struct termp *p, term_margin head, 5295c635efSGarrett D'Amore term_margin foot, const void *arg) 5395c635efSGarrett D'Amore { 5495c635efSGarrett D'Amore 5595c635efSGarrett D'Amore p->headf = head; 5695c635efSGarrett D'Amore p->footf = foot; 5795c635efSGarrett D'Amore p->argf = arg; 5895c635efSGarrett D'Amore (*p->begin)(p); 5995c635efSGarrett D'Amore } 6095c635efSGarrett D'Amore 6195c635efSGarrett D'Amore void 6295c635efSGarrett D'Amore term_end(struct termp *p) 6395c635efSGarrett D'Amore { 6495c635efSGarrett D'Amore 6595c635efSGarrett D'Amore (*p->end)(p); 6695c635efSGarrett D'Amore } 6795c635efSGarrett D'Amore 6895c635efSGarrett D'Amore /* 69*260e9a87SYuri Pankov * Flush a chunk of text. By default, break the output line each time 70*260e9a87SYuri Pankov * the right margin is reached, and continue output on the next line 71*260e9a87SYuri Pankov * at the same offset as the chunk itself. By default, also break the 72*260e9a87SYuri Pankov * output line at the end of the chunk. 7395c635efSGarrett D'Amore * The following flags may be specified: 7495c635efSGarrett D'Amore * 75*260e9a87SYuri Pankov * - TERMP_NOBREAK: Do not break the output line at the right margin, 76*260e9a87SYuri Pankov * but only at the max right margin. Also, do not break the output 77*260e9a87SYuri Pankov * line at the end of the chunk, such that the next call can pad to 78*260e9a87SYuri Pankov * the next column. However, if less than p->trailspace blanks, 79*260e9a87SYuri Pankov * which can be 0, 1, or 2, remain to the right margin, the line 80*260e9a87SYuri Pankov * will be broken. 81*260e9a87SYuri Pankov * - TERMP_BRIND: If the chunk does not fit and the output line has 82*260e9a87SYuri Pankov * to be broken, start the next line at the right margin instead 83*260e9a87SYuri Pankov * of at the offset. Used together with TERMP_NOBREAK for the tags 84*260e9a87SYuri Pankov * in various kinds of tagged lists. 85*260e9a87SYuri Pankov * - TERMP_DANGLE: Do not break the output line at the right margin, 86*260e9a87SYuri Pankov * append the next chunk after it even if this one is too long. 87*260e9a87SYuri Pankov * To be used together with TERMP_NOBREAK. 88*260e9a87SYuri Pankov * - TERMP_HANG: Like TERMP_DANGLE, and also suppress padding before 89*260e9a87SYuri Pankov * the next chunk if this column is not full. 9095c635efSGarrett D'Amore */ 9195c635efSGarrett D'Amore void 9295c635efSGarrett D'Amore term_flushln(struct termp *p) 9395c635efSGarrett D'Amore { 94698f87a4SGarrett D'Amore size_t i; /* current input position in p->buf */ 95698f87a4SGarrett D'Amore int ntab; /* number of tabs to prepend */ 9695c635efSGarrett D'Amore size_t vis; /* current visual position on output */ 9795c635efSGarrett D'Amore size_t vbl; /* number of blanks to prepend to output */ 9895c635efSGarrett D'Amore size_t vend; /* end of word visual position on output */ 9995c635efSGarrett D'Amore size_t bp; /* visual right border position */ 10095c635efSGarrett D'Amore size_t dv; /* temporary for visual pos calculations */ 101698f87a4SGarrett D'Amore size_t j; /* temporary loop index for p->buf */ 102698f87a4SGarrett D'Amore size_t jhy; /* last hyph before overflow w/r/t j */ 10395c635efSGarrett D'Amore size_t maxvis; /* output position of visible boundary */ 10495c635efSGarrett D'Amore 10595c635efSGarrett D'Amore /* 10695c635efSGarrett D'Amore * First, establish the maximum columns of "visible" content. 10795c635efSGarrett D'Amore * This is usually the difference between the right-margin and 10895c635efSGarrett D'Amore * an indentation, but can be, for tagged lists or columns, a 10995c635efSGarrett D'Amore * small set of values. 110698f87a4SGarrett D'Amore * 111698f87a4SGarrett D'Amore * The following unsigned-signed subtractions look strange, 112698f87a4SGarrett D'Amore * but they are actually correct. If the int p->overstep 113698f87a4SGarrett D'Amore * is negative, it gets sign extended. Subtracting that 114698f87a4SGarrett D'Amore * very large size_t effectively adds a small number to dv. 11595c635efSGarrett D'Amore */ 116*260e9a87SYuri Pankov dv = p->rmargin > p->offset ? p->rmargin - p->offset : 0; 11795c635efSGarrett D'Amore maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 11895c635efSGarrett D'Amore 119*260e9a87SYuri Pankov if (p->flags & TERMP_NOBREAK) { 120*260e9a87SYuri Pankov dv = p->maxrmargin > p->offset ? 121*260e9a87SYuri Pankov p->maxrmargin - p->offset : 0; 122*260e9a87SYuri Pankov bp = (int)dv > p->overstep ? 123*260e9a87SYuri Pankov dv - (size_t)p->overstep : 0; 124*260e9a87SYuri Pankov } else 125*260e9a87SYuri Pankov bp = maxvis; 12695c635efSGarrett D'Amore 12795c635efSGarrett D'Amore /* 12895c635efSGarrett D'Amore * Calculate the required amount of padding. 12995c635efSGarrett D'Amore */ 13095c635efSGarrett D'Amore vbl = p->offset + p->overstep > p->viscol ? 13195c635efSGarrett D'Amore p->offset + p->overstep - p->viscol : 0; 13295c635efSGarrett D'Amore 13395c635efSGarrett D'Amore vis = vend = 0; 13495c635efSGarrett D'Amore i = 0; 13595c635efSGarrett D'Amore 13695c635efSGarrett D'Amore while (i < p->col) { 13795c635efSGarrett D'Amore /* 13895c635efSGarrett D'Amore * Handle literal tab characters: collapse all 13995c635efSGarrett D'Amore * subsequent tabs into a single huge set of spaces. 14095c635efSGarrett D'Amore */ 141698f87a4SGarrett D'Amore ntab = 0; 14295c635efSGarrett D'Amore while (i < p->col && '\t' == p->buf[i]) { 14395c635efSGarrett D'Amore vend = (vis / p->tabwidth + 1) * p->tabwidth; 14495c635efSGarrett D'Amore vbl += vend - vis; 14595c635efSGarrett D'Amore vis = vend; 146698f87a4SGarrett D'Amore ntab++; 14795c635efSGarrett D'Amore i++; 14895c635efSGarrett D'Amore } 14995c635efSGarrett D'Amore 15095c635efSGarrett D'Amore /* 15195c635efSGarrett D'Amore * Count up visible word characters. Control sequences 15295c635efSGarrett D'Amore * (starting with the CSI) aren't counted. A space 15395c635efSGarrett D'Amore * generates a non-printing word, which is valid (the 15495c635efSGarrett D'Amore * space is printed according to regular spacing rules). 15595c635efSGarrett D'Amore */ 15695c635efSGarrett D'Amore 15795c635efSGarrett D'Amore for (j = i, jhy = 0; j < p->col; j++) { 158698f87a4SGarrett D'Amore if (' ' == p->buf[j] || '\t' == p->buf[j]) 15995c635efSGarrett D'Amore break; 16095c635efSGarrett D'Amore 16195c635efSGarrett D'Amore /* Back over the the last printed character. */ 16295c635efSGarrett D'Amore if (8 == p->buf[j]) { 16395c635efSGarrett D'Amore assert(j); 16495c635efSGarrett D'Amore vend -= (*p->width)(p, p->buf[j - 1]); 16595c635efSGarrett D'Amore continue; 16695c635efSGarrett D'Amore } 16795c635efSGarrett D'Amore 16895c635efSGarrett D'Amore /* Regular word. */ 16995c635efSGarrett D'Amore /* Break at the hyphen point if we overrun. */ 17095c635efSGarrett D'Amore if (vend > vis && vend < bp && 171*260e9a87SYuri Pankov (ASCII_HYPH == p->buf[j] || 172*260e9a87SYuri Pankov ASCII_BREAK == p->buf[j])) 17395c635efSGarrett D'Amore jhy = j; 17495c635efSGarrett D'Amore 175*260e9a87SYuri Pankov /* 176*260e9a87SYuri Pankov * Hyphenation now decided, put back a real 177*260e9a87SYuri Pankov * hyphen such that we get the correct width. 178*260e9a87SYuri Pankov */ 179*260e9a87SYuri Pankov if (ASCII_HYPH == p->buf[j]) 180*260e9a87SYuri Pankov p->buf[j] = '-'; 181*260e9a87SYuri Pankov 18295c635efSGarrett D'Amore vend += (*p->width)(p, p->buf[j]); 18395c635efSGarrett D'Amore } 18495c635efSGarrett D'Amore 18595c635efSGarrett D'Amore /* 18695c635efSGarrett D'Amore * Find out whether we would exceed the right margin. 18795c635efSGarrett D'Amore * If so, break to the next line. 18895c635efSGarrett D'Amore */ 18995c635efSGarrett D'Amore if (vend > bp && 0 == jhy && vis > 0) { 19095c635efSGarrett D'Amore vend -= vis; 19195c635efSGarrett D'Amore (*p->endline)(p); 19295c635efSGarrett D'Amore p->viscol = 0; 193*260e9a87SYuri Pankov if (TERMP_BRIND & p->flags) { 19495c635efSGarrett D'Amore vbl = p->rmargin; 195*260e9a87SYuri Pankov vend += p->rmargin; 196*260e9a87SYuri Pankov vend -= p->offset; 19795c635efSGarrett D'Amore } else 19895c635efSGarrett D'Amore vbl = p->offset; 19995c635efSGarrett D'Amore 200698f87a4SGarrett D'Amore /* use pending tabs on the new line */ 201698f87a4SGarrett D'Amore 202698f87a4SGarrett D'Amore if (0 < ntab) 203698f87a4SGarrett D'Amore vbl += ntab * p->tabwidth; 204698f87a4SGarrett D'Amore 205698f87a4SGarrett D'Amore /* 206698f87a4SGarrett D'Amore * Remove the p->overstep width. 207698f87a4SGarrett D'Amore * Again, if p->overstep is negative, 208698f87a4SGarrett D'Amore * sign extension does the right thing. 209698f87a4SGarrett D'Amore */ 21095c635efSGarrett D'Amore 21195c635efSGarrett D'Amore bp += (size_t)p->overstep; 21295c635efSGarrett D'Amore p->overstep = 0; 21395c635efSGarrett D'Amore } 21495c635efSGarrett D'Amore 21595c635efSGarrett D'Amore /* Write out the [remaining] word. */ 21695c635efSGarrett D'Amore for ( ; i < p->col; i++) { 21795c635efSGarrett D'Amore if (vend > bp && jhy > 0 && i > jhy) 21895c635efSGarrett D'Amore break; 21995c635efSGarrett D'Amore if ('\t' == p->buf[i]) 22095c635efSGarrett D'Amore break; 22195c635efSGarrett D'Amore if (' ' == p->buf[i]) { 22295c635efSGarrett D'Amore j = i; 223*260e9a87SYuri Pankov while (i < p->col && ' ' == p->buf[i]) 22495c635efSGarrett D'Amore i++; 225698f87a4SGarrett D'Amore dv = (i - j) * (*p->width)(p, ' '); 22695c635efSGarrett D'Amore vbl += dv; 22795c635efSGarrett D'Amore vend += dv; 22895c635efSGarrett D'Amore break; 22995c635efSGarrett D'Amore } 23095c635efSGarrett D'Amore if (ASCII_NBRSP == p->buf[i]) { 23195c635efSGarrett D'Amore vbl += (*p->width)(p, ' '); 23295c635efSGarrett D'Amore continue; 23395c635efSGarrett D'Amore } 234*260e9a87SYuri Pankov if (ASCII_BREAK == p->buf[i]) 235*260e9a87SYuri Pankov continue; 23695c635efSGarrett D'Amore 23795c635efSGarrett D'Amore /* 23895c635efSGarrett D'Amore * Now we definitely know there will be 23995c635efSGarrett D'Amore * printable characters to output, 24095c635efSGarrett D'Amore * so write preceding white space now. 24195c635efSGarrett D'Amore */ 24295c635efSGarrett D'Amore if (vbl) { 24395c635efSGarrett D'Amore (*p->advance)(p, vbl); 24495c635efSGarrett D'Amore p->viscol += vbl; 24595c635efSGarrett D'Amore vbl = 0; 24695c635efSGarrett D'Amore } 24795c635efSGarrett D'Amore 24895c635efSGarrett D'Amore (*p->letter)(p, p->buf[i]); 24995c635efSGarrett D'Amore if (8 == p->buf[i]) 25095c635efSGarrett D'Amore p->viscol -= (*p->width)(p, p->buf[i-1]); 25195c635efSGarrett D'Amore else 25295c635efSGarrett D'Amore p->viscol += (*p->width)(p, p->buf[i]); 25395c635efSGarrett D'Amore } 25495c635efSGarrett D'Amore vis = vend; 25595c635efSGarrett D'Amore } 25695c635efSGarrett D'Amore 25795c635efSGarrett D'Amore /* 25895c635efSGarrett D'Amore * If there was trailing white space, it was not printed; 25995c635efSGarrett D'Amore * so reset the cursor position accordingly. 26095c635efSGarrett D'Amore */ 261*260e9a87SYuri Pankov if (vis > vbl) 26295c635efSGarrett D'Amore vis -= vbl; 263*260e9a87SYuri Pankov else 264*260e9a87SYuri Pankov vis = 0; 26595c635efSGarrett D'Amore 26695c635efSGarrett D'Amore p->col = 0; 26795c635efSGarrett D'Amore p->overstep = 0; 26895c635efSGarrett D'Amore 26995c635efSGarrett D'Amore if ( ! (TERMP_NOBREAK & p->flags)) { 27095c635efSGarrett D'Amore p->viscol = 0; 27195c635efSGarrett D'Amore (*p->endline)(p); 27295c635efSGarrett D'Amore return; 27395c635efSGarrett D'Amore } 27495c635efSGarrett D'Amore 27595c635efSGarrett D'Amore if (TERMP_HANG & p->flags) { 276*260e9a87SYuri Pankov p->overstep += (int)(p->offset + vis - p->rmargin + 277698f87a4SGarrett D'Amore p->trailspace * (*p->width)(p, ' ')); 27895c635efSGarrett D'Amore 27995c635efSGarrett D'Amore /* 28095c635efSGarrett D'Amore * If we have overstepped the margin, temporarily move 28195c635efSGarrett D'Amore * it to the right and flag the rest of the line to be 28295c635efSGarrett D'Amore * shorter. 283698f87a4SGarrett D'Amore * If there is a request to keep the columns together, 284698f87a4SGarrett D'Amore * allow negative overstep when the column is not full. 28595c635efSGarrett D'Amore */ 286698f87a4SGarrett D'Amore if (p->trailspace && p->overstep < 0) 28795c635efSGarrett D'Amore p->overstep = 0; 28895c635efSGarrett D'Amore return; 28995c635efSGarrett D'Amore 29095c635efSGarrett D'Amore } else if (TERMP_DANGLE & p->flags) 29195c635efSGarrett D'Amore return; 29295c635efSGarrett D'Amore 29395c635efSGarrett D'Amore /* If the column was overrun, break the line. */ 294698f87a4SGarrett D'Amore if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { 29595c635efSGarrett D'Amore (*p->endline)(p); 29695c635efSGarrett D'Amore p->viscol = 0; 29795c635efSGarrett D'Amore } 29895c635efSGarrett D'Amore } 29995c635efSGarrett D'Amore 30095c635efSGarrett D'Amore /* 30195c635efSGarrett D'Amore * A newline only breaks an existing line; it won't assert vertical 30295c635efSGarrett D'Amore * space. All data in the output buffer is flushed prior to the newline 30395c635efSGarrett D'Amore * assertion. 30495c635efSGarrett D'Amore */ 30595c635efSGarrett D'Amore void 30695c635efSGarrett D'Amore term_newln(struct termp *p) 30795c635efSGarrett D'Amore { 30895c635efSGarrett D'Amore 30995c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 31095c635efSGarrett D'Amore if (p->col || p->viscol) 31195c635efSGarrett D'Amore term_flushln(p); 31295c635efSGarrett D'Amore } 31395c635efSGarrett D'Amore 31495c635efSGarrett D'Amore /* 31595c635efSGarrett D'Amore * Asserts a vertical space (a full, empty line-break between lines). 31695c635efSGarrett D'Amore * Note that if used twice, this will cause two blank spaces and so on. 31795c635efSGarrett D'Amore * All data in the output buffer is flushed prior to the newline 31895c635efSGarrett D'Amore * assertion. 31995c635efSGarrett D'Amore */ 32095c635efSGarrett D'Amore void 32195c635efSGarrett D'Amore term_vspace(struct termp *p) 32295c635efSGarrett D'Amore { 32395c635efSGarrett D'Amore 32495c635efSGarrett D'Amore term_newln(p); 32595c635efSGarrett D'Amore p->viscol = 0; 326698f87a4SGarrett D'Amore if (0 < p->skipvsp) 327698f87a4SGarrett D'Amore p->skipvsp--; 328698f87a4SGarrett D'Amore else 32995c635efSGarrett D'Amore (*p->endline)(p); 33095c635efSGarrett D'Amore } 33195c635efSGarrett D'Amore 332*260e9a87SYuri Pankov /* Swap current and previous font; for \fP and .ft P */ 33395c635efSGarrett D'Amore void 33495c635efSGarrett D'Amore term_fontlast(struct termp *p) 33595c635efSGarrett D'Amore { 33695c635efSGarrett D'Amore enum termfont f; 33795c635efSGarrett D'Amore 33895c635efSGarrett D'Amore f = p->fontl; 33995c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 34095c635efSGarrett D'Amore p->fontq[p->fonti] = f; 34195c635efSGarrett D'Amore } 34295c635efSGarrett D'Amore 343*260e9a87SYuri Pankov /* Set font, save current, discard previous; for \f, .ft, .B etc. */ 34495c635efSGarrett D'Amore void 34595c635efSGarrett D'Amore term_fontrepl(struct termp *p, enum termfont f) 34695c635efSGarrett D'Amore { 34795c635efSGarrett D'Amore 34895c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 34995c635efSGarrett D'Amore p->fontq[p->fonti] = f; 35095c635efSGarrett D'Amore } 35195c635efSGarrett D'Amore 352*260e9a87SYuri Pankov /* Set font, save previous. */ 35395c635efSGarrett D'Amore void 35495c635efSGarrett D'Amore term_fontpush(struct termp *p, enum termfont f) 35595c635efSGarrett D'Amore { 35695c635efSGarrett D'Amore 35795c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 358*260e9a87SYuri Pankov if (++p->fonti == p->fontsz) { 359*260e9a87SYuri Pankov p->fontsz += 8; 360*260e9a87SYuri Pankov p->fontq = mandoc_reallocarray(p->fontq, 361*260e9a87SYuri Pankov p->fontsz, sizeof(enum termfont *)); 362*260e9a87SYuri Pankov } 363*260e9a87SYuri Pankov p->fontq[p->fonti] = f; 36495c635efSGarrett D'Amore } 36595c635efSGarrett D'Amore 366*260e9a87SYuri Pankov /* Flush to make the saved pointer current again. */ 36795c635efSGarrett D'Amore void 368*260e9a87SYuri Pankov term_fontpopq(struct termp *p, int i) 36995c635efSGarrett D'Amore { 37095c635efSGarrett D'Amore 371*260e9a87SYuri Pankov assert(i >= 0); 372*260e9a87SYuri Pankov if (p->fonti > i) 373*260e9a87SYuri Pankov p->fonti = i; 37495c635efSGarrett D'Amore } 37595c635efSGarrett D'Amore 376*260e9a87SYuri Pankov /* Pop one font off the stack. */ 37795c635efSGarrett D'Amore void 37895c635efSGarrett D'Amore term_fontpop(struct termp *p) 37995c635efSGarrett D'Amore { 38095c635efSGarrett D'Amore 38195c635efSGarrett D'Amore assert(p->fonti); 38295c635efSGarrett D'Amore p->fonti--; 38395c635efSGarrett D'Amore } 38495c635efSGarrett D'Amore 38595c635efSGarrett D'Amore /* 38695c635efSGarrett D'Amore * Handle pwords, partial words, which may be either a single word or a 38795c635efSGarrett D'Amore * phrase that cannot be broken down (such as a literal string). This 38895c635efSGarrett D'Amore * handles word styling. 38995c635efSGarrett D'Amore */ 39095c635efSGarrett D'Amore void 39195c635efSGarrett D'Amore term_word(struct termp *p, const char *word) 39295c635efSGarrett D'Amore { 393698f87a4SGarrett D'Amore const char nbrsp[2] = { ASCII_NBRSP, 0 }; 39495c635efSGarrett D'Amore const char *seq, *cp; 39595c635efSGarrett D'Amore int sz, uc; 39695c635efSGarrett D'Amore size_t ssz; 39795c635efSGarrett D'Amore enum mandoc_esc esc; 39895c635efSGarrett D'Amore 39995c635efSGarrett D'Amore if ( ! (TERMP_NOSPACE & p->flags)) { 40095c635efSGarrett D'Amore if ( ! (TERMP_KEEP & p->flags)) { 40195c635efSGarrett D'Amore bufferc(p, ' '); 40295c635efSGarrett D'Amore if (TERMP_SENTENCE & p->flags) 40395c635efSGarrett D'Amore bufferc(p, ' '); 40495c635efSGarrett D'Amore } else 40595c635efSGarrett D'Amore bufferc(p, ASCII_NBRSP); 40695c635efSGarrett D'Amore } 407698f87a4SGarrett D'Amore if (TERMP_PREKEEP & p->flags) 408698f87a4SGarrett D'Amore p->flags |= TERMP_KEEP; 40995c635efSGarrett D'Amore 41095c635efSGarrett D'Amore if ( ! (p->flags & TERMP_NONOSPACE)) 41195c635efSGarrett D'Amore p->flags &= ~TERMP_NOSPACE; 41295c635efSGarrett D'Amore else 41395c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 41495c635efSGarrett D'Amore 415*260e9a87SYuri Pankov p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); 416*260e9a87SYuri Pankov p->skipvsp = 0; 41795c635efSGarrett D'Amore 41895c635efSGarrett D'Amore while ('\0' != *word) { 419698f87a4SGarrett D'Amore if ('\\' != *word) { 420698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 421698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 422698f87a4SGarrett D'Amore word++; 42395c635efSGarrett D'Amore continue; 424698f87a4SGarrett D'Amore } 425698f87a4SGarrett D'Amore if (TERMP_NBRWORD & p->flags) { 426698f87a4SGarrett D'Amore if (' ' == *word) { 427698f87a4SGarrett D'Amore encode(p, nbrsp, 1); 428698f87a4SGarrett D'Amore word++; 429698f87a4SGarrett D'Amore continue; 430698f87a4SGarrett D'Amore } 431698f87a4SGarrett D'Amore ssz = strcspn(word, "\\ "); 432698f87a4SGarrett D'Amore } else 433698f87a4SGarrett D'Amore ssz = strcspn(word, "\\"); 434698f87a4SGarrett D'Amore encode(p, word, ssz); 435698f87a4SGarrett D'Amore word += (int)ssz; 436698f87a4SGarrett D'Amore continue; 437698f87a4SGarrett D'Amore } 43895c635efSGarrett D'Amore 43995c635efSGarrett D'Amore word++; 44095c635efSGarrett D'Amore esc = mandoc_escape(&word, &seq, &sz); 44195c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 442*260e9a87SYuri Pankov continue; 44395c635efSGarrett D'Amore 44495c635efSGarrett D'Amore switch (esc) { 445*260e9a87SYuri Pankov case ESCAPE_UNICODE: 44695c635efSGarrett D'Amore uc = mchars_num2uc(seq + 1, sz - 1); 44795c635efSGarrett D'Amore break; 448*260e9a87SYuri Pankov case ESCAPE_NUMBERED: 449*260e9a87SYuri Pankov uc = mchars_num2char(seq, sz); 450*260e9a87SYuri Pankov if (uc < 0) 45195c635efSGarrett D'Amore continue; 45295c635efSGarrett D'Amore break; 453*260e9a87SYuri Pankov case ESCAPE_SPECIAL: 454*260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 455*260e9a87SYuri Pankov cp = mchars_spec2str(p->symtab, 456*260e9a87SYuri Pankov seq, sz, &ssz); 457*260e9a87SYuri Pankov if (cp != NULL) 45895c635efSGarrett D'Amore encode(p, cp, ssz); 459*260e9a87SYuri Pankov } else { 460*260e9a87SYuri Pankov uc = mchars_spec2cp(p->symtab, seq, sz); 461*260e9a87SYuri Pankov if (uc > 0) 462*260e9a87SYuri Pankov encode1(p, uc); 463*260e9a87SYuri Pankov } 464*260e9a87SYuri Pankov continue; 465*260e9a87SYuri Pankov case ESCAPE_FONTBOLD: 46695c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_BOLD); 467*260e9a87SYuri Pankov continue; 468*260e9a87SYuri Pankov case ESCAPE_FONTITALIC: 46995c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_UNDER); 470*260e9a87SYuri Pankov continue; 471*260e9a87SYuri Pankov case ESCAPE_FONTBI: 472698f87a4SGarrett D'Amore term_fontrepl(p, TERMFONT_BI); 473*260e9a87SYuri Pankov continue; 474*260e9a87SYuri Pankov case ESCAPE_FONT: 47595c635efSGarrett D'Amore /* FALLTHROUGH */ 476*260e9a87SYuri Pankov case ESCAPE_FONTROMAN: 47795c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_NONE); 478*260e9a87SYuri Pankov continue; 479*260e9a87SYuri Pankov case ESCAPE_FONTPREV: 48095c635efSGarrett D'Amore term_fontlast(p); 481*260e9a87SYuri Pankov continue; 482*260e9a87SYuri Pankov case ESCAPE_NOSPACE: 483698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) 484698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 485698f87a4SGarrett D'Amore else if ('\0' == *word) 486*260e9a87SYuri Pankov p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); 487*260e9a87SYuri Pankov continue; 488*260e9a87SYuri Pankov case ESCAPE_SKIPCHAR: 489698f87a4SGarrett D'Amore p->flags |= TERMP_SKIPCHAR; 490*260e9a87SYuri Pankov continue; 491*260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE: 492*260e9a87SYuri Pankov cp = seq + sz; 493*260e9a87SYuri Pankov while (seq < cp) { 494*260e9a87SYuri Pankov if (*seq == '\\') { 495*260e9a87SYuri Pankov mandoc_escape(&seq, NULL, NULL); 496*260e9a87SYuri Pankov continue; 497*260e9a87SYuri Pankov } 498*260e9a87SYuri Pankov encode1(p, *seq++); 499*260e9a87SYuri Pankov if (seq < cp) 500*260e9a87SYuri Pankov encode(p, "\b", 1); 501*260e9a87SYuri Pankov } 50295c635efSGarrett D'Amore default: 503*260e9a87SYuri Pankov continue; 504*260e9a87SYuri Pankov } 505*260e9a87SYuri Pankov 506*260e9a87SYuri Pankov /* 507*260e9a87SYuri Pankov * Common handling for Unicode and numbered 508*260e9a87SYuri Pankov * character escape sequences. 509*260e9a87SYuri Pankov */ 510*260e9a87SYuri Pankov 511*260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 512*260e9a87SYuri Pankov cp = ascii_uc2str(uc); 513*260e9a87SYuri Pankov encode(p, cp, strlen(cp)); 514*260e9a87SYuri Pankov } else { 515*260e9a87SYuri Pankov if ((uc < 0x20 && uc != 0x09) || 516*260e9a87SYuri Pankov (uc > 0x7E && uc < 0xA0)) 517*260e9a87SYuri Pankov uc = 0xFFFD; 518*260e9a87SYuri Pankov encode1(p, uc); 51995c635efSGarrett D'Amore } 52095c635efSGarrett D'Amore } 521698f87a4SGarrett D'Amore p->flags &= ~TERMP_NBRWORD; 52295c635efSGarrett D'Amore } 52395c635efSGarrett D'Amore 52495c635efSGarrett D'Amore static void 525698f87a4SGarrett D'Amore adjbuf(struct termp *p, size_t sz) 52695c635efSGarrett D'Amore { 52795c635efSGarrett D'Amore 52895c635efSGarrett D'Amore if (0 == p->maxcols) 52995c635efSGarrett D'Amore p->maxcols = 1024; 53095c635efSGarrett D'Amore while (sz >= p->maxcols) 53195c635efSGarrett D'Amore p->maxcols <<= 2; 53295c635efSGarrett D'Amore 533*260e9a87SYuri Pankov p->buf = mandoc_reallocarray(p->buf, p->maxcols, sizeof(int)); 53495c635efSGarrett D'Amore } 53595c635efSGarrett D'Amore 53695c635efSGarrett D'Amore static void 53795c635efSGarrett D'Amore bufferc(struct termp *p, char c) 53895c635efSGarrett D'Amore { 53995c635efSGarrett D'Amore 54095c635efSGarrett D'Amore if (p->col + 1 >= p->maxcols) 54195c635efSGarrett D'Amore adjbuf(p, p->col + 1); 54295c635efSGarrett D'Amore 54395c635efSGarrett D'Amore p->buf[p->col++] = c; 54495c635efSGarrett D'Amore } 54595c635efSGarrett D'Amore 54695c635efSGarrett D'Amore /* 54795c635efSGarrett D'Amore * See encode(). 54895c635efSGarrett D'Amore * Do this for a single (probably unicode) value. 54995c635efSGarrett D'Amore * Does not check for non-decorated glyphs. 55095c635efSGarrett D'Amore */ 55195c635efSGarrett D'Amore static void 55295c635efSGarrett D'Amore encode1(struct termp *p, int c) 55395c635efSGarrett D'Amore { 55495c635efSGarrett D'Amore enum termfont f; 55595c635efSGarrett D'Amore 556698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 557698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 558698f87a4SGarrett D'Amore return; 559698f87a4SGarrett D'Amore } 560698f87a4SGarrett D'Amore 561698f87a4SGarrett D'Amore if (p->col + 6 >= p->maxcols) 562698f87a4SGarrett D'Amore adjbuf(p, p->col + 6); 56395c635efSGarrett D'Amore 564*260e9a87SYuri Pankov f = p->fontq[p->fonti]; 56595c635efSGarrett D'Amore 566698f87a4SGarrett D'Amore if (TERMFONT_UNDER == f || TERMFONT_BI == f) { 56795c635efSGarrett D'Amore p->buf[p->col++] = '_'; 56895c635efSGarrett D'Amore p->buf[p->col++] = 8; 569698f87a4SGarrett D'Amore } 570698f87a4SGarrett D'Amore if (TERMFONT_BOLD == f || TERMFONT_BI == f) { 571698f87a4SGarrett D'Amore if (ASCII_HYPH == c) 572698f87a4SGarrett D'Amore p->buf[p->col++] = '-'; 573698f87a4SGarrett D'Amore else 574698f87a4SGarrett D'Amore p->buf[p->col++] = c; 575698f87a4SGarrett D'Amore p->buf[p->col++] = 8; 576698f87a4SGarrett D'Amore } 57795c635efSGarrett D'Amore p->buf[p->col++] = c; 57895c635efSGarrett D'Amore } 57995c635efSGarrett D'Amore 58095c635efSGarrett D'Amore static void 58195c635efSGarrett D'Amore encode(struct termp *p, const char *word, size_t sz) 58295c635efSGarrett D'Amore { 583698f87a4SGarrett D'Amore size_t i; 58495c635efSGarrett D'Amore 585698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 586698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 587698f87a4SGarrett D'Amore return; 588698f87a4SGarrett D'Amore } 58995c635efSGarrett D'Amore 59095c635efSGarrett D'Amore /* 59195c635efSGarrett D'Amore * Encode and buffer a string of characters. If the current 59295c635efSGarrett D'Amore * font mode is unset, buffer directly, else encode then buffer 59395c635efSGarrett D'Amore * character by character. 59495c635efSGarrett D'Amore */ 59595c635efSGarrett D'Amore 596*260e9a87SYuri Pankov if (p->fontq[p->fonti] == TERMFONT_NONE) { 597698f87a4SGarrett D'Amore if (p->col + sz >= p->maxcols) 598698f87a4SGarrett D'Amore adjbuf(p, p->col + sz); 599698f87a4SGarrett D'Amore for (i = 0; i < sz; i++) 60095c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 60195c635efSGarrett D'Amore return; 60295c635efSGarrett D'Amore } 60395c635efSGarrett D'Amore 60495c635efSGarrett D'Amore /* Pre-buffer, assuming worst-case. */ 60595c635efSGarrett D'Amore 606698f87a4SGarrett D'Amore if (p->col + 1 + (sz * 5) >= p->maxcols) 607698f87a4SGarrett D'Amore adjbuf(p, p->col + 1 + (sz * 5)); 60895c635efSGarrett D'Amore 609698f87a4SGarrett D'Amore for (i = 0; i < sz; i++) { 610698f87a4SGarrett D'Amore if (ASCII_HYPH == word[i] || 611698f87a4SGarrett D'Amore isgraph((unsigned char)word[i])) 612698f87a4SGarrett D'Amore encode1(p, word[i]); 61395c635efSGarrett D'Amore else 61495c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 61595c635efSGarrett D'Amore } 61695c635efSGarrett D'Amore } 61795c635efSGarrett D'Amore 618*260e9a87SYuri Pankov void 619*260e9a87SYuri Pankov term_setwidth(struct termp *p, const char *wstr) 620*260e9a87SYuri Pankov { 621*260e9a87SYuri Pankov struct roffsu su; 622*260e9a87SYuri Pankov size_t width; 623*260e9a87SYuri Pankov int iop; 624*260e9a87SYuri Pankov 625*260e9a87SYuri Pankov iop = 0; 626*260e9a87SYuri Pankov width = 0; 627*260e9a87SYuri Pankov if (NULL != wstr) { 628*260e9a87SYuri Pankov switch (*wstr) { 629*260e9a87SYuri Pankov case '+': 630*260e9a87SYuri Pankov iop = 1; 631*260e9a87SYuri Pankov wstr++; 632*260e9a87SYuri Pankov break; 633*260e9a87SYuri Pankov case '-': 634*260e9a87SYuri Pankov iop = -1; 635*260e9a87SYuri Pankov wstr++; 636*260e9a87SYuri Pankov break; 637*260e9a87SYuri Pankov default: 638*260e9a87SYuri Pankov break; 639*260e9a87SYuri Pankov } 640*260e9a87SYuri Pankov if (a2roffsu(wstr, &su, SCALE_MAX)) 641*260e9a87SYuri Pankov width = term_hspan(p, &su); 642*260e9a87SYuri Pankov else 643*260e9a87SYuri Pankov iop = 0; 644*260e9a87SYuri Pankov } 645*260e9a87SYuri Pankov (*p->setwidth)(p, iop, width); 646*260e9a87SYuri Pankov } 647*260e9a87SYuri Pankov 64895c635efSGarrett D'Amore size_t 64995c635efSGarrett D'Amore term_len(const struct termp *p, size_t sz) 65095c635efSGarrett D'Amore { 65195c635efSGarrett D'Amore 65295c635efSGarrett D'Amore return((*p->width)(p, ' ') * sz); 65395c635efSGarrett D'Amore } 65495c635efSGarrett D'Amore 655698f87a4SGarrett D'Amore static size_t 656698f87a4SGarrett D'Amore cond_width(const struct termp *p, int c, int *skip) 657698f87a4SGarrett D'Amore { 658698f87a4SGarrett D'Amore 659698f87a4SGarrett D'Amore if (*skip) { 660698f87a4SGarrett D'Amore (*skip) = 0; 661698f87a4SGarrett D'Amore return(0); 662698f87a4SGarrett D'Amore } else 663698f87a4SGarrett D'Amore return((*p->width)(p, c)); 664698f87a4SGarrett D'Amore } 66595c635efSGarrett D'Amore 66695c635efSGarrett D'Amore size_t 66795c635efSGarrett D'Amore term_strlen(const struct termp *p, const char *cp) 66895c635efSGarrett D'Amore { 66995c635efSGarrett D'Amore size_t sz, rsz, i; 670*260e9a87SYuri Pankov int ssz, skip, uc; 67195c635efSGarrett D'Amore const char *seq, *rhs; 67295c635efSGarrett D'Amore enum mandoc_esc esc; 673*260e9a87SYuri Pankov static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH, 674*260e9a87SYuri Pankov ASCII_BREAK, '\0' }; 67595c635efSGarrett D'Amore 67695c635efSGarrett D'Amore /* 67795c635efSGarrett D'Amore * Account for escaped sequences within string length 67895c635efSGarrett D'Amore * calculations. This follows the logic in term_word() as we 67995c635efSGarrett D'Amore * must calculate the width of produced strings. 68095c635efSGarrett D'Amore */ 68195c635efSGarrett D'Amore 68295c635efSGarrett D'Amore sz = 0; 683698f87a4SGarrett D'Amore skip = 0; 68495c635efSGarrett D'Amore while ('\0' != *cp) { 68595c635efSGarrett D'Amore rsz = strcspn(cp, rej); 68695c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 687698f87a4SGarrett D'Amore sz += cond_width(p, *cp++, &skip); 68895c635efSGarrett D'Amore 68995c635efSGarrett D'Amore switch (*cp) { 690*260e9a87SYuri Pankov case '\\': 69195c635efSGarrett D'Amore cp++; 69295c635efSGarrett D'Amore esc = mandoc_escape(&cp, &seq, &ssz); 69395c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 69495c635efSGarrett D'Amore continue; 69595c635efSGarrett D'Amore 69695c635efSGarrett D'Amore rhs = NULL; 69795c635efSGarrett D'Amore 69895c635efSGarrett D'Amore switch (esc) { 699*260e9a87SYuri Pankov case ESCAPE_UNICODE: 700*260e9a87SYuri Pankov uc = mchars_num2uc(seq + 1, ssz - 1); 70195c635efSGarrett D'Amore break; 702*260e9a87SYuri Pankov case ESCAPE_NUMBERED: 703*260e9a87SYuri Pankov uc = mchars_num2char(seq, ssz); 704*260e9a87SYuri Pankov if (uc < 0) 705*260e9a87SYuri Pankov continue; 70695c635efSGarrett D'Amore break; 707*260e9a87SYuri Pankov case ESCAPE_SPECIAL: 708*260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 709*260e9a87SYuri Pankov rhs = mchars_spec2str(p->symtab, 710*260e9a87SYuri Pankov seq, ssz, &rsz); 711*260e9a87SYuri Pankov if (rhs != NULL) 71295c635efSGarrett D'Amore break; 713*260e9a87SYuri Pankov } else { 714*260e9a87SYuri Pankov uc = mchars_spec2cp(p->symtab, 715*260e9a87SYuri Pankov seq, ssz); 716*260e9a87SYuri Pankov if (uc > 0) 717*260e9a87SYuri Pankov sz += cond_width(p, uc, &skip); 718*260e9a87SYuri Pankov } 719*260e9a87SYuri Pankov continue; 720*260e9a87SYuri Pankov case ESCAPE_SKIPCHAR: 721698f87a4SGarrett D'Amore skip = 1; 722*260e9a87SYuri Pankov continue; 723*260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE: 724*260e9a87SYuri Pankov rsz = 0; 725*260e9a87SYuri Pankov rhs = seq + ssz; 726*260e9a87SYuri Pankov while (seq < rhs) { 727*260e9a87SYuri Pankov if (*seq == '\\') { 728*260e9a87SYuri Pankov mandoc_escape(&seq, NULL, NULL); 729*260e9a87SYuri Pankov continue; 730*260e9a87SYuri Pankov } 731*260e9a87SYuri Pankov i = (*p->width)(p, *seq++); 732*260e9a87SYuri Pankov if (rsz < i) 733*260e9a87SYuri Pankov rsz = i; 734*260e9a87SYuri Pankov } 735*260e9a87SYuri Pankov sz += rsz; 736*260e9a87SYuri Pankov continue; 73795c635efSGarrett D'Amore default: 738*260e9a87SYuri Pankov continue; 73995c635efSGarrett D'Amore } 74095c635efSGarrett D'Amore 741*260e9a87SYuri Pankov /* 742*260e9a87SYuri Pankov * Common handling for Unicode and numbered 743*260e9a87SYuri Pankov * character escape sequences. 744*260e9a87SYuri Pankov */ 745*260e9a87SYuri Pankov 746*260e9a87SYuri Pankov if (rhs == NULL) { 747*260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 748*260e9a87SYuri Pankov rhs = ascii_uc2str(uc); 749*260e9a87SYuri Pankov rsz = strlen(rhs); 750*260e9a87SYuri Pankov } else { 751*260e9a87SYuri Pankov if ((uc < 0x20 && uc != 0x09) || 752*260e9a87SYuri Pankov (uc > 0x7E && uc < 0xA0)) 753*260e9a87SYuri Pankov uc = 0xFFFD; 754*260e9a87SYuri Pankov sz += cond_width(p, uc, &skip); 755*260e9a87SYuri Pankov continue; 756*260e9a87SYuri Pankov } 757*260e9a87SYuri Pankov } 75895c635efSGarrett D'Amore 759698f87a4SGarrett D'Amore if (skip) { 760698f87a4SGarrett D'Amore skip = 0; 761698f87a4SGarrett D'Amore break; 762698f87a4SGarrett D'Amore } 763698f87a4SGarrett D'Amore 764*260e9a87SYuri Pankov /* 765*260e9a87SYuri Pankov * Common handling for all escape sequences 766*260e9a87SYuri Pankov * printing more than one character. 767*260e9a87SYuri Pankov */ 768*260e9a87SYuri Pankov 76995c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 77095c635efSGarrett D'Amore sz += (*p->width)(p, *rhs++); 77195c635efSGarrett D'Amore break; 772*260e9a87SYuri Pankov case ASCII_NBRSP: 773698f87a4SGarrett D'Amore sz += cond_width(p, ' ', &skip); 77495c635efSGarrett D'Amore cp++; 77595c635efSGarrett D'Amore break; 776*260e9a87SYuri Pankov case ASCII_HYPH: 777698f87a4SGarrett D'Amore sz += cond_width(p, '-', &skip); 77895c635efSGarrett D'Amore cp++; 779*260e9a87SYuri Pankov /* FALLTHROUGH */ 780*260e9a87SYuri Pankov case ASCII_BREAK: 78195c635efSGarrett D'Amore break; 78295c635efSGarrett D'Amore default: 78395c635efSGarrett D'Amore break; 78495c635efSGarrett D'Amore } 78595c635efSGarrett D'Amore } 78695c635efSGarrett D'Amore 78795c635efSGarrett D'Amore return(sz); 78895c635efSGarrett D'Amore } 78995c635efSGarrett D'Amore 790*260e9a87SYuri Pankov int 79195c635efSGarrett D'Amore term_vspan(const struct termp *p, const struct roffsu *su) 79295c635efSGarrett D'Amore { 79395c635efSGarrett D'Amore double r; 794*260e9a87SYuri Pankov int ri; 79595c635efSGarrett D'Amore 79695c635efSGarrett D'Amore switch (su->unit) { 797*260e9a87SYuri Pankov case SCALE_BU: 798*260e9a87SYuri Pankov r = su->scale / 40.0; 79995c635efSGarrett D'Amore break; 800*260e9a87SYuri Pankov case SCALE_CM: 801*260e9a87SYuri Pankov r = su->scale * 6.0 / 2.54; 80295c635efSGarrett D'Amore break; 803*260e9a87SYuri Pankov case SCALE_FS: 804*260e9a87SYuri Pankov r = su->scale * 65536.0 / 40.0; 805*260e9a87SYuri Pankov break; 806*260e9a87SYuri Pankov case SCALE_IN: 807*260e9a87SYuri Pankov r = su->scale * 6.0; 808*260e9a87SYuri Pankov break; 809*260e9a87SYuri Pankov case SCALE_MM: 810*260e9a87SYuri Pankov r = su->scale * 0.006; 811*260e9a87SYuri Pankov break; 812*260e9a87SYuri Pankov case SCALE_PC: 81395c635efSGarrett D'Amore r = su->scale; 81495c635efSGarrett D'Amore break; 815*260e9a87SYuri Pankov case SCALE_PT: 816*260e9a87SYuri Pankov r = su->scale / 12.0; 81795c635efSGarrett D'Amore break; 818*260e9a87SYuri Pankov case SCALE_EN: 819*260e9a87SYuri Pankov /* FALLTHROUGH */ 820*260e9a87SYuri Pankov case SCALE_EM: 821*260e9a87SYuri Pankov r = su->scale * 0.6; 82295c635efSGarrett D'Amore break; 823*260e9a87SYuri Pankov case SCALE_VS: 82495c635efSGarrett D'Amore r = su->scale; 82595c635efSGarrett D'Amore break; 82695c635efSGarrett D'Amore default: 827*260e9a87SYuri Pankov abort(); 828*260e9a87SYuri Pankov /* NOTREACHED */ 829*260e9a87SYuri Pankov } 830*260e9a87SYuri Pankov ri = r > 0.0 ? r + 0.4995 : r - 0.4995; 831*260e9a87SYuri Pankov return(ri < 66 ? ri : 1); 83295c635efSGarrett D'Amore } 83395c635efSGarrett D'Amore 834*260e9a87SYuri Pankov int 83595c635efSGarrett D'Amore term_hspan(const struct termp *p, const struct roffsu *su) 83695c635efSGarrett D'Amore { 83795c635efSGarrett D'Amore double v; 83895c635efSGarrett D'Amore 839*260e9a87SYuri Pankov v = (*p->hspan)(p, su); 840*260e9a87SYuri Pankov return(v > 0.0 ? v + 0.0005 : v - 0.0005); 84195c635efSGarrett D'Amore } 842