1*698f87a4SGarrett D'Amore /* $Id: term.c,v 1.214 2013/12/25 00:39:31 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 395c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*698f87a4SGarrett D'Amore * Copyright (c) 2010, 2011, 2012, 2013 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 #ifdef HAVE_CONFIG_H 1995c635efSGarrett D'Amore #include "config.h" 2095c635efSGarrett D'Amore #endif 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <sys/types.h> 2395c635efSGarrett D'Amore 2495c635efSGarrett D'Amore #include <assert.h> 2595c635efSGarrett D'Amore #include <ctype.h> 2695c635efSGarrett D'Amore #include <stdint.h> 2795c635efSGarrett D'Amore #include <stdio.h> 2895c635efSGarrett D'Amore #include <stdlib.h> 2995c635efSGarrett D'Amore #include <string.h> 3095c635efSGarrett D'Amore 3195c635efSGarrett D'Amore #include "mandoc.h" 3295c635efSGarrett D'Amore #include "out.h" 3395c635efSGarrett D'Amore #include "term.h" 3495c635efSGarrett D'Amore #include "main.h" 3595c635efSGarrett D'Amore 36*698f87a4SGarrett D'Amore static size_t cond_width(const struct termp *, int, int *); 37*698f87a4SGarrett D'Amore static void adjbuf(struct termp *p, size_t); 3895c635efSGarrett D'Amore static void bufferc(struct termp *, char); 3995c635efSGarrett D'Amore static void encode(struct termp *, const char *, size_t); 4095c635efSGarrett D'Amore static void encode1(struct termp *, int); 4195c635efSGarrett D'Amore 4295c635efSGarrett D'Amore void 4395c635efSGarrett D'Amore term_free(struct termp *p) 4495c635efSGarrett D'Amore { 4595c635efSGarrett D'Amore 4695c635efSGarrett D'Amore if (p->buf) 4795c635efSGarrett D'Amore free(p->buf); 4895c635efSGarrett D'Amore if (p->symtab) 4995c635efSGarrett D'Amore mchars_free(p->symtab); 5095c635efSGarrett D'Amore 5195c635efSGarrett D'Amore free(p); 5295c635efSGarrett D'Amore } 5395c635efSGarrett D'Amore 5495c635efSGarrett D'Amore 5595c635efSGarrett D'Amore void 5695c635efSGarrett D'Amore term_begin(struct termp *p, term_margin head, 5795c635efSGarrett D'Amore term_margin foot, const void *arg) 5895c635efSGarrett D'Amore { 5995c635efSGarrett D'Amore 6095c635efSGarrett D'Amore p->headf = head; 6195c635efSGarrett D'Amore p->footf = foot; 6295c635efSGarrett D'Amore p->argf = arg; 6395c635efSGarrett D'Amore (*p->begin)(p); 6495c635efSGarrett D'Amore } 6595c635efSGarrett D'Amore 6695c635efSGarrett D'Amore 6795c635efSGarrett D'Amore void 6895c635efSGarrett D'Amore term_end(struct termp *p) 6995c635efSGarrett D'Amore { 7095c635efSGarrett D'Amore 7195c635efSGarrett D'Amore (*p->end)(p); 7295c635efSGarrett D'Amore } 7395c635efSGarrett D'Amore 7495c635efSGarrett D'Amore /* 7595c635efSGarrett D'Amore * Flush a line of text. A "line" is loosely defined as being something 7695c635efSGarrett D'Amore * that should be followed by a newline, regardless of whether it's 7795c635efSGarrett D'Amore * broken apart by newlines getting there. A line can also be a 7895c635efSGarrett D'Amore * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does 7995c635efSGarrett D'Amore * not have a trailing newline. 8095c635efSGarrett D'Amore * 8195c635efSGarrett D'Amore * The following flags may be specified: 8295c635efSGarrett D'Amore * 8395c635efSGarrett D'Amore * - TERMP_NOBREAK: this is the most important and is used when making 8495c635efSGarrett D'Amore * columns. In short: don't print a newline and instead expect the 8595c635efSGarrett D'Amore * next call to do the padding up to the start of the next column. 86*698f87a4SGarrett D'Amore * p->trailspace may be set to 0, 1, or 2, depending on how many 87*698f87a4SGarrett D'Amore * space characters are required at the end of the column. 8895c635efSGarrett D'Amore * 8995c635efSGarrett D'Amore * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and 9095c635efSGarrett D'Amore * the line is overrun, and don't pad-right if it's underrun. 9195c635efSGarrett D'Amore * 9295c635efSGarrett D'Amore * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when 9395c635efSGarrett D'Amore * overrunning, instead save the position and continue at that point 9495c635efSGarrett D'Amore * when the next invocation. 9595c635efSGarrett D'Amore * 9695c635efSGarrett D'Amore * In-line line breaking: 9795c635efSGarrett D'Amore * 9895c635efSGarrett D'Amore * If TERMP_NOBREAK is specified and the line overruns the right 9995c635efSGarrett D'Amore * margin, it will break and pad-right to the right margin after 10095c635efSGarrett D'Amore * writing. If maxrmargin is violated, it will break and continue 10195c635efSGarrett D'Amore * writing from the right-margin, which will lead to the above scenario 10295c635efSGarrett D'Amore * upon exit. Otherwise, the line will break at the right margin. 10395c635efSGarrett D'Amore */ 10495c635efSGarrett D'Amore void 10595c635efSGarrett D'Amore term_flushln(struct termp *p) 10695c635efSGarrett D'Amore { 107*698f87a4SGarrett D'Amore size_t i; /* current input position in p->buf */ 108*698f87a4SGarrett D'Amore int ntab; /* number of tabs to prepend */ 10995c635efSGarrett D'Amore size_t vis; /* current visual position on output */ 11095c635efSGarrett D'Amore size_t vbl; /* number of blanks to prepend to output */ 11195c635efSGarrett D'Amore size_t vend; /* end of word visual position on output */ 11295c635efSGarrett D'Amore size_t bp; /* visual right border position */ 11395c635efSGarrett D'Amore size_t dv; /* temporary for visual pos calculations */ 114*698f87a4SGarrett D'Amore size_t j; /* temporary loop index for p->buf */ 115*698f87a4SGarrett D'Amore size_t jhy; /* last hyph before overflow w/r/t j */ 11695c635efSGarrett D'Amore size_t maxvis; /* output position of visible boundary */ 11795c635efSGarrett D'Amore size_t mmax; /* used in calculating bp */ 11895c635efSGarrett D'Amore 11995c635efSGarrett D'Amore /* 12095c635efSGarrett D'Amore * First, establish the maximum columns of "visible" content. 12195c635efSGarrett D'Amore * This is usually the difference between the right-margin and 12295c635efSGarrett D'Amore * an indentation, but can be, for tagged lists or columns, a 12395c635efSGarrett D'Amore * small set of values. 124*698f87a4SGarrett D'Amore * 125*698f87a4SGarrett D'Amore * The following unsigned-signed subtractions look strange, 126*698f87a4SGarrett D'Amore * but they are actually correct. If the int p->overstep 127*698f87a4SGarrett D'Amore * is negative, it gets sign extended. Subtracting that 128*698f87a4SGarrett D'Amore * very large size_t effectively adds a small number to dv. 12995c635efSGarrett D'Amore */ 13095c635efSGarrett D'Amore assert (p->rmargin >= p->offset); 13195c635efSGarrett D'Amore dv = p->rmargin - p->offset; 13295c635efSGarrett D'Amore maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 13395c635efSGarrett D'Amore dv = p->maxrmargin - p->offset; 13495c635efSGarrett D'Amore mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 13595c635efSGarrett D'Amore 13695c635efSGarrett D'Amore bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; 13795c635efSGarrett D'Amore 13895c635efSGarrett D'Amore /* 13995c635efSGarrett D'Amore * Calculate the required amount of padding. 14095c635efSGarrett D'Amore */ 14195c635efSGarrett D'Amore vbl = p->offset + p->overstep > p->viscol ? 14295c635efSGarrett D'Amore p->offset + p->overstep - p->viscol : 0; 14395c635efSGarrett D'Amore 14495c635efSGarrett D'Amore vis = vend = 0; 14595c635efSGarrett D'Amore i = 0; 14695c635efSGarrett D'Amore 14795c635efSGarrett D'Amore while (i < p->col) { 14895c635efSGarrett D'Amore /* 14995c635efSGarrett D'Amore * Handle literal tab characters: collapse all 15095c635efSGarrett D'Amore * subsequent tabs into a single huge set of spaces. 15195c635efSGarrett D'Amore */ 152*698f87a4SGarrett D'Amore ntab = 0; 15395c635efSGarrett D'Amore while (i < p->col && '\t' == p->buf[i]) { 15495c635efSGarrett D'Amore vend = (vis / p->tabwidth + 1) * p->tabwidth; 15595c635efSGarrett D'Amore vbl += vend - vis; 15695c635efSGarrett D'Amore vis = vend; 157*698f87a4SGarrett D'Amore ntab++; 15895c635efSGarrett D'Amore i++; 15995c635efSGarrett D'Amore } 16095c635efSGarrett D'Amore 16195c635efSGarrett D'Amore /* 16295c635efSGarrett D'Amore * Count up visible word characters. Control sequences 16395c635efSGarrett D'Amore * (starting with the CSI) aren't counted. A space 16495c635efSGarrett D'Amore * generates a non-printing word, which is valid (the 16595c635efSGarrett D'Amore * space is printed according to regular spacing rules). 16695c635efSGarrett D'Amore */ 16795c635efSGarrett D'Amore 16895c635efSGarrett D'Amore for (j = i, jhy = 0; j < p->col; j++) { 169*698f87a4SGarrett D'Amore if (' ' == p->buf[j] || '\t' == p->buf[j]) 17095c635efSGarrett D'Amore break; 17195c635efSGarrett D'Amore 17295c635efSGarrett D'Amore /* Back over the the last printed character. */ 17395c635efSGarrett D'Amore if (8 == p->buf[j]) { 17495c635efSGarrett D'Amore assert(j); 17595c635efSGarrett D'Amore vend -= (*p->width)(p, p->buf[j - 1]); 17695c635efSGarrett D'Amore continue; 17795c635efSGarrett D'Amore } 17895c635efSGarrett D'Amore 17995c635efSGarrett D'Amore /* Regular word. */ 18095c635efSGarrett D'Amore /* Break at the hyphen point if we overrun. */ 18195c635efSGarrett D'Amore if (vend > vis && vend < bp && 18295c635efSGarrett D'Amore ASCII_HYPH == p->buf[j]) 18395c635efSGarrett D'Amore jhy = j; 18495c635efSGarrett D'Amore 18595c635efSGarrett D'Amore vend += (*p->width)(p, p->buf[j]); 18695c635efSGarrett D'Amore } 18795c635efSGarrett D'Amore 18895c635efSGarrett D'Amore /* 18995c635efSGarrett D'Amore * Find out whether we would exceed the right margin. 19095c635efSGarrett D'Amore * If so, break to the next line. 19195c635efSGarrett D'Amore */ 19295c635efSGarrett D'Amore if (vend > bp && 0 == jhy && vis > 0) { 19395c635efSGarrett D'Amore vend -= vis; 19495c635efSGarrett D'Amore (*p->endline)(p); 19595c635efSGarrett D'Amore p->viscol = 0; 19695c635efSGarrett D'Amore if (TERMP_NOBREAK & p->flags) { 19795c635efSGarrett D'Amore vbl = p->rmargin; 19895c635efSGarrett D'Amore vend += p->rmargin - p->offset; 19995c635efSGarrett D'Amore } else 20095c635efSGarrett D'Amore vbl = p->offset; 20195c635efSGarrett D'Amore 202*698f87a4SGarrett D'Amore /* use pending tabs on the new line */ 203*698f87a4SGarrett D'Amore 204*698f87a4SGarrett D'Amore if (0 < ntab) 205*698f87a4SGarrett D'Amore vbl += ntab * p->tabwidth; 206*698f87a4SGarrett D'Amore 207*698f87a4SGarrett D'Amore /* 208*698f87a4SGarrett D'Amore * Remove the p->overstep width. 209*698f87a4SGarrett D'Amore * Again, if p->overstep is negative, 210*698f87a4SGarrett D'Amore * sign extension does the right thing. 211*698f87a4SGarrett D'Amore */ 21295c635efSGarrett D'Amore 21395c635efSGarrett D'Amore bp += (size_t)p->overstep; 21495c635efSGarrett D'Amore p->overstep = 0; 21595c635efSGarrett D'Amore } 21695c635efSGarrett D'Amore 21795c635efSGarrett D'Amore /* Write out the [remaining] word. */ 21895c635efSGarrett D'Amore for ( ; i < p->col; i++) { 21995c635efSGarrett D'Amore if (vend > bp && jhy > 0 && i > jhy) 22095c635efSGarrett D'Amore break; 22195c635efSGarrett D'Amore if ('\t' == p->buf[i]) 22295c635efSGarrett D'Amore break; 22395c635efSGarrett D'Amore if (' ' == p->buf[i]) { 22495c635efSGarrett D'Amore j = i; 22595c635efSGarrett D'Amore while (' ' == p->buf[i]) 22695c635efSGarrett D'Amore i++; 227*698f87a4SGarrett D'Amore dv = (i - j) * (*p->width)(p, ' '); 22895c635efSGarrett D'Amore vbl += dv; 22995c635efSGarrett D'Amore vend += dv; 23095c635efSGarrett D'Amore break; 23195c635efSGarrett D'Amore } 23295c635efSGarrett D'Amore if (ASCII_NBRSP == p->buf[i]) { 23395c635efSGarrett D'Amore vbl += (*p->width)(p, ' '); 23495c635efSGarrett D'Amore continue; 23595c635efSGarrett D'Amore } 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 if (ASCII_HYPH == p->buf[i]) { 24995c635efSGarrett D'Amore (*p->letter)(p, '-'); 25095c635efSGarrett D'Amore p->viscol += (*p->width)(p, '-'); 25195c635efSGarrett D'Amore continue; 25295c635efSGarrett D'Amore } 25395c635efSGarrett D'Amore 25495c635efSGarrett D'Amore (*p->letter)(p, p->buf[i]); 25595c635efSGarrett D'Amore if (8 == p->buf[i]) 25695c635efSGarrett D'Amore p->viscol -= (*p->width)(p, p->buf[i-1]); 25795c635efSGarrett D'Amore else 25895c635efSGarrett D'Amore p->viscol += (*p->width)(p, p->buf[i]); 25995c635efSGarrett D'Amore } 26095c635efSGarrett D'Amore vis = vend; 26195c635efSGarrett D'Amore } 26295c635efSGarrett D'Amore 26395c635efSGarrett D'Amore /* 26495c635efSGarrett D'Amore * If there was trailing white space, it was not printed; 26595c635efSGarrett D'Amore * so reset the cursor position accordingly. 26695c635efSGarrett D'Amore */ 26795c635efSGarrett D'Amore if (vis) 26895c635efSGarrett D'Amore vis -= vbl; 26995c635efSGarrett D'Amore 27095c635efSGarrett D'Amore p->col = 0; 27195c635efSGarrett D'Amore p->overstep = 0; 27295c635efSGarrett D'Amore 27395c635efSGarrett D'Amore if ( ! (TERMP_NOBREAK & p->flags)) { 27495c635efSGarrett D'Amore p->viscol = 0; 27595c635efSGarrett D'Amore (*p->endline)(p); 27695c635efSGarrett D'Amore return; 27795c635efSGarrett D'Amore } 27895c635efSGarrett D'Amore 27995c635efSGarrett D'Amore if (TERMP_HANG & p->flags) { 280*698f87a4SGarrett D'Amore p->overstep = (int)(vis - maxvis + 281*698f87a4SGarrett D'Amore p->trailspace * (*p->width)(p, ' ')); 28295c635efSGarrett D'Amore 28395c635efSGarrett D'Amore /* 28495c635efSGarrett D'Amore * If we have overstepped the margin, temporarily move 28595c635efSGarrett D'Amore * it to the right and flag the rest of the line to be 28695c635efSGarrett D'Amore * shorter. 287*698f87a4SGarrett D'Amore * If there is a request to keep the columns together, 288*698f87a4SGarrett D'Amore * allow negative overstep when the column is not full. 28995c635efSGarrett D'Amore */ 290*698f87a4SGarrett D'Amore if (p->trailspace && p->overstep < 0) 29195c635efSGarrett D'Amore p->overstep = 0; 29295c635efSGarrett D'Amore return; 29395c635efSGarrett D'Amore 29495c635efSGarrett D'Amore } else if (TERMP_DANGLE & p->flags) 29595c635efSGarrett D'Amore return; 29695c635efSGarrett D'Amore 29795c635efSGarrett D'Amore /* If the column was overrun, break the line. */ 298*698f87a4SGarrett D'Amore if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { 29995c635efSGarrett D'Amore (*p->endline)(p); 30095c635efSGarrett D'Amore p->viscol = 0; 30195c635efSGarrett D'Amore } 30295c635efSGarrett D'Amore } 30395c635efSGarrett D'Amore 30495c635efSGarrett D'Amore 30595c635efSGarrett D'Amore /* 30695c635efSGarrett D'Amore * A newline only breaks an existing line; it won't assert vertical 30795c635efSGarrett D'Amore * space. All data in the output buffer is flushed prior to the newline 30895c635efSGarrett D'Amore * assertion. 30995c635efSGarrett D'Amore */ 31095c635efSGarrett D'Amore void 31195c635efSGarrett D'Amore term_newln(struct termp *p) 31295c635efSGarrett D'Amore { 31395c635efSGarrett D'Amore 31495c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 31595c635efSGarrett D'Amore if (p->col || p->viscol) 31695c635efSGarrett D'Amore term_flushln(p); 31795c635efSGarrett D'Amore } 31895c635efSGarrett D'Amore 31995c635efSGarrett D'Amore 32095c635efSGarrett D'Amore /* 32195c635efSGarrett D'Amore * Asserts a vertical space (a full, empty line-break between lines). 32295c635efSGarrett D'Amore * Note that if used twice, this will cause two blank spaces and so on. 32395c635efSGarrett D'Amore * All data in the output buffer is flushed prior to the newline 32495c635efSGarrett D'Amore * assertion. 32595c635efSGarrett D'Amore */ 32695c635efSGarrett D'Amore void 32795c635efSGarrett D'Amore term_vspace(struct termp *p) 32895c635efSGarrett D'Amore { 32995c635efSGarrett D'Amore 33095c635efSGarrett D'Amore term_newln(p); 33195c635efSGarrett D'Amore p->viscol = 0; 332*698f87a4SGarrett D'Amore if (0 < p->skipvsp) 333*698f87a4SGarrett D'Amore p->skipvsp--; 334*698f87a4SGarrett D'Amore else 33595c635efSGarrett D'Amore (*p->endline)(p); 33695c635efSGarrett D'Amore } 33795c635efSGarrett D'Amore 33895c635efSGarrett D'Amore void 33995c635efSGarrett D'Amore term_fontlast(struct termp *p) 34095c635efSGarrett D'Amore { 34195c635efSGarrett D'Amore enum termfont f; 34295c635efSGarrett D'Amore 34395c635efSGarrett D'Amore f = p->fontl; 34495c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 34595c635efSGarrett D'Amore p->fontq[p->fonti] = f; 34695c635efSGarrett D'Amore } 34795c635efSGarrett D'Amore 34895c635efSGarrett D'Amore 34995c635efSGarrett D'Amore void 35095c635efSGarrett D'Amore term_fontrepl(struct termp *p, enum termfont f) 35195c635efSGarrett D'Amore { 35295c635efSGarrett D'Amore 35395c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 35495c635efSGarrett D'Amore p->fontq[p->fonti] = f; 35595c635efSGarrett D'Amore } 35695c635efSGarrett D'Amore 35795c635efSGarrett D'Amore 35895c635efSGarrett D'Amore void 35995c635efSGarrett D'Amore term_fontpush(struct termp *p, enum termfont f) 36095c635efSGarrett D'Amore { 36195c635efSGarrett D'Amore 36295c635efSGarrett D'Amore assert(p->fonti + 1 < 10); 36395c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 36495c635efSGarrett D'Amore p->fontq[++p->fonti] = f; 36595c635efSGarrett D'Amore } 36695c635efSGarrett D'Amore 36795c635efSGarrett D'Amore 36895c635efSGarrett D'Amore const void * 36995c635efSGarrett D'Amore term_fontq(struct termp *p) 37095c635efSGarrett D'Amore { 37195c635efSGarrett D'Amore 37295c635efSGarrett D'Amore return(&p->fontq[p->fonti]); 37395c635efSGarrett D'Amore } 37495c635efSGarrett D'Amore 37595c635efSGarrett D'Amore 37695c635efSGarrett D'Amore enum termfont 37795c635efSGarrett D'Amore term_fonttop(struct termp *p) 37895c635efSGarrett D'Amore { 37995c635efSGarrett D'Amore 38095c635efSGarrett D'Amore return(p->fontq[p->fonti]); 38195c635efSGarrett D'Amore } 38295c635efSGarrett D'Amore 38395c635efSGarrett D'Amore 38495c635efSGarrett D'Amore void 38595c635efSGarrett D'Amore term_fontpopq(struct termp *p, const void *key) 38695c635efSGarrett D'Amore { 38795c635efSGarrett D'Amore 388*698f87a4SGarrett D'Amore while (p->fonti >= 0 && key < (void *)(p->fontq + p->fonti)) 38995c635efSGarrett D'Amore p->fonti--; 39095c635efSGarrett D'Amore assert(p->fonti >= 0); 39195c635efSGarrett D'Amore } 39295c635efSGarrett D'Amore 39395c635efSGarrett D'Amore 39495c635efSGarrett D'Amore void 39595c635efSGarrett D'Amore term_fontpop(struct termp *p) 39695c635efSGarrett D'Amore { 39795c635efSGarrett D'Amore 39895c635efSGarrett D'Amore assert(p->fonti); 39995c635efSGarrett D'Amore p->fonti--; 40095c635efSGarrett D'Amore } 40195c635efSGarrett D'Amore 40295c635efSGarrett D'Amore /* 40395c635efSGarrett D'Amore * Handle pwords, partial words, which may be either a single word or a 40495c635efSGarrett D'Amore * phrase that cannot be broken down (such as a literal string). This 40595c635efSGarrett D'Amore * handles word styling. 40695c635efSGarrett D'Amore */ 40795c635efSGarrett D'Amore void 40895c635efSGarrett D'Amore term_word(struct termp *p, const char *word) 40995c635efSGarrett D'Amore { 410*698f87a4SGarrett D'Amore const char nbrsp[2] = { ASCII_NBRSP, 0 }; 41195c635efSGarrett D'Amore const char *seq, *cp; 41295c635efSGarrett D'Amore char c; 41395c635efSGarrett D'Amore int sz, uc; 41495c635efSGarrett D'Amore size_t ssz; 41595c635efSGarrett D'Amore enum mandoc_esc esc; 41695c635efSGarrett D'Amore 41795c635efSGarrett D'Amore if ( ! (TERMP_NOSPACE & p->flags)) { 41895c635efSGarrett D'Amore if ( ! (TERMP_KEEP & p->flags)) { 41995c635efSGarrett D'Amore bufferc(p, ' '); 42095c635efSGarrett D'Amore if (TERMP_SENTENCE & p->flags) 42195c635efSGarrett D'Amore bufferc(p, ' '); 42295c635efSGarrett D'Amore } else 42395c635efSGarrett D'Amore bufferc(p, ASCII_NBRSP); 42495c635efSGarrett D'Amore } 425*698f87a4SGarrett D'Amore if (TERMP_PREKEEP & p->flags) 426*698f87a4SGarrett D'Amore p->flags |= TERMP_KEEP; 42795c635efSGarrett D'Amore 42895c635efSGarrett D'Amore if ( ! (p->flags & TERMP_NONOSPACE)) 42995c635efSGarrett D'Amore p->flags &= ~TERMP_NOSPACE; 43095c635efSGarrett D'Amore else 43195c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 43295c635efSGarrett D'Amore 433*698f87a4SGarrett D'Amore p->flags &= ~TERMP_SENTENCE; 43495c635efSGarrett D'Amore 43595c635efSGarrett D'Amore while ('\0' != *word) { 436*698f87a4SGarrett D'Amore if ('\\' != *word) { 437*698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 438*698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 439*698f87a4SGarrett D'Amore word++; 44095c635efSGarrett D'Amore continue; 441*698f87a4SGarrett D'Amore } 442*698f87a4SGarrett D'Amore if (TERMP_NBRWORD & p->flags) { 443*698f87a4SGarrett D'Amore if (' ' == *word) { 444*698f87a4SGarrett D'Amore encode(p, nbrsp, 1); 445*698f87a4SGarrett D'Amore word++; 446*698f87a4SGarrett D'Amore continue; 447*698f87a4SGarrett D'Amore } 448*698f87a4SGarrett D'Amore ssz = strcspn(word, "\\ "); 449*698f87a4SGarrett D'Amore } else 450*698f87a4SGarrett D'Amore ssz = strcspn(word, "\\"); 451*698f87a4SGarrett D'Amore encode(p, word, ssz); 452*698f87a4SGarrett D'Amore word += (int)ssz; 453*698f87a4SGarrett D'Amore continue; 454*698f87a4SGarrett D'Amore } 45595c635efSGarrett D'Amore 45695c635efSGarrett D'Amore word++; 45795c635efSGarrett D'Amore esc = mandoc_escape(&word, &seq, &sz); 45895c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 45995c635efSGarrett D'Amore break; 46095c635efSGarrett D'Amore 46195c635efSGarrett D'Amore if (TERMENC_ASCII != p->enc) 46295c635efSGarrett D'Amore switch (esc) { 46395c635efSGarrett D'Amore case (ESCAPE_UNICODE): 46495c635efSGarrett D'Amore uc = mchars_num2uc(seq + 1, sz - 1); 46595c635efSGarrett D'Amore if ('\0' == uc) 46695c635efSGarrett D'Amore break; 46795c635efSGarrett D'Amore encode1(p, uc); 46895c635efSGarrett D'Amore continue; 46995c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 47095c635efSGarrett D'Amore uc = mchars_spec2cp(p->symtab, seq, sz); 47195c635efSGarrett D'Amore if (uc <= 0) 47295c635efSGarrett D'Amore break; 47395c635efSGarrett D'Amore encode1(p, uc); 47495c635efSGarrett D'Amore continue; 47595c635efSGarrett D'Amore default: 47695c635efSGarrett D'Amore break; 47795c635efSGarrett D'Amore } 47895c635efSGarrett D'Amore 47995c635efSGarrett D'Amore switch (esc) { 48095c635efSGarrett D'Amore case (ESCAPE_UNICODE): 48195c635efSGarrett D'Amore encode1(p, '?'); 48295c635efSGarrett D'Amore break; 48395c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 48495c635efSGarrett D'Amore c = mchars_num2char(seq, sz); 48595c635efSGarrett D'Amore if ('\0' != c) 48695c635efSGarrett D'Amore encode(p, &c, 1); 48795c635efSGarrett D'Amore break; 48895c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 48995c635efSGarrett D'Amore cp = mchars_spec2str(p->symtab, seq, sz, &ssz); 49095c635efSGarrett D'Amore if (NULL != cp) 49195c635efSGarrett D'Amore encode(p, cp, ssz); 49295c635efSGarrett D'Amore else if (1 == ssz) 49395c635efSGarrett D'Amore encode(p, seq, sz); 49495c635efSGarrett D'Amore break; 49595c635efSGarrett D'Amore case (ESCAPE_FONTBOLD): 49695c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_BOLD); 49795c635efSGarrett D'Amore break; 49895c635efSGarrett D'Amore case (ESCAPE_FONTITALIC): 49995c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_UNDER); 50095c635efSGarrett D'Amore break; 501*698f87a4SGarrett D'Amore case (ESCAPE_FONTBI): 502*698f87a4SGarrett D'Amore term_fontrepl(p, TERMFONT_BI); 503*698f87a4SGarrett D'Amore break; 50495c635efSGarrett D'Amore case (ESCAPE_FONT): 50595c635efSGarrett D'Amore /* FALLTHROUGH */ 50695c635efSGarrett D'Amore case (ESCAPE_FONTROMAN): 50795c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_NONE); 50895c635efSGarrett D'Amore break; 50995c635efSGarrett D'Amore case (ESCAPE_FONTPREV): 51095c635efSGarrett D'Amore term_fontlast(p); 51195c635efSGarrett D'Amore break; 51295c635efSGarrett D'Amore case (ESCAPE_NOSPACE): 513*698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) 514*698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 515*698f87a4SGarrett D'Amore else if ('\0' == *word) 51695c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 51795c635efSGarrett D'Amore break; 518*698f87a4SGarrett D'Amore case (ESCAPE_SKIPCHAR): 519*698f87a4SGarrett D'Amore p->flags |= TERMP_SKIPCHAR; 520*698f87a4SGarrett D'Amore break; 52195c635efSGarrett D'Amore default: 52295c635efSGarrett D'Amore break; 52395c635efSGarrett D'Amore } 52495c635efSGarrett D'Amore } 525*698f87a4SGarrett D'Amore p->flags &= ~TERMP_NBRWORD; 52695c635efSGarrett D'Amore } 52795c635efSGarrett D'Amore 52895c635efSGarrett D'Amore static void 529*698f87a4SGarrett D'Amore adjbuf(struct termp *p, size_t sz) 53095c635efSGarrett D'Amore { 53195c635efSGarrett D'Amore 53295c635efSGarrett D'Amore if (0 == p->maxcols) 53395c635efSGarrett D'Amore p->maxcols = 1024; 53495c635efSGarrett D'Amore while (sz >= p->maxcols) 53595c635efSGarrett D'Amore p->maxcols <<= 2; 53695c635efSGarrett D'Amore 537*698f87a4SGarrett D'Amore p->buf = mandoc_realloc(p->buf, sizeof(int) * p->maxcols); 53895c635efSGarrett D'Amore } 53995c635efSGarrett D'Amore 54095c635efSGarrett D'Amore static void 54195c635efSGarrett D'Amore bufferc(struct termp *p, char c) 54295c635efSGarrett D'Amore { 54395c635efSGarrett D'Amore 54495c635efSGarrett D'Amore if (p->col + 1 >= p->maxcols) 54595c635efSGarrett D'Amore adjbuf(p, p->col + 1); 54695c635efSGarrett D'Amore 54795c635efSGarrett D'Amore p->buf[p->col++] = c; 54895c635efSGarrett D'Amore } 54995c635efSGarrett D'Amore 55095c635efSGarrett D'Amore /* 55195c635efSGarrett D'Amore * See encode(). 55295c635efSGarrett D'Amore * Do this for a single (probably unicode) value. 55395c635efSGarrett D'Amore * Does not check for non-decorated glyphs. 55495c635efSGarrett D'Amore */ 55595c635efSGarrett D'Amore static void 55695c635efSGarrett D'Amore encode1(struct termp *p, int c) 55795c635efSGarrett D'Amore { 55895c635efSGarrett D'Amore enum termfont f; 55995c635efSGarrett D'Amore 560*698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 561*698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 562*698f87a4SGarrett D'Amore return; 563*698f87a4SGarrett D'Amore } 564*698f87a4SGarrett D'Amore 565*698f87a4SGarrett D'Amore if (p->col + 6 >= p->maxcols) 566*698f87a4SGarrett D'Amore adjbuf(p, p->col + 6); 56795c635efSGarrett D'Amore 56895c635efSGarrett D'Amore f = term_fonttop(p); 56995c635efSGarrett D'Amore 570*698f87a4SGarrett D'Amore if (TERMFONT_UNDER == f || TERMFONT_BI == f) { 57195c635efSGarrett D'Amore p->buf[p->col++] = '_'; 57295c635efSGarrett D'Amore p->buf[p->col++] = 8; 573*698f87a4SGarrett D'Amore } 574*698f87a4SGarrett D'Amore if (TERMFONT_BOLD == f || TERMFONT_BI == f) { 575*698f87a4SGarrett D'Amore if (ASCII_HYPH == c) 576*698f87a4SGarrett D'Amore p->buf[p->col++] = '-'; 577*698f87a4SGarrett D'Amore else 578*698f87a4SGarrett D'Amore p->buf[p->col++] = c; 579*698f87a4SGarrett D'Amore p->buf[p->col++] = 8; 580*698f87a4SGarrett D'Amore } 58195c635efSGarrett D'Amore p->buf[p->col++] = c; 58295c635efSGarrett D'Amore } 58395c635efSGarrett D'Amore 58495c635efSGarrett D'Amore static void 58595c635efSGarrett D'Amore encode(struct termp *p, const char *word, size_t sz) 58695c635efSGarrett D'Amore { 587*698f87a4SGarrett D'Amore size_t i; 58895c635efSGarrett D'Amore 589*698f87a4SGarrett D'Amore if (TERMP_SKIPCHAR & p->flags) { 590*698f87a4SGarrett D'Amore p->flags &= ~TERMP_SKIPCHAR; 591*698f87a4SGarrett D'Amore return; 592*698f87a4SGarrett D'Amore } 59395c635efSGarrett D'Amore 59495c635efSGarrett D'Amore /* 59595c635efSGarrett D'Amore * Encode and buffer a string of characters. If the current 59695c635efSGarrett D'Amore * font mode is unset, buffer directly, else encode then buffer 59795c635efSGarrett D'Amore * character by character. 59895c635efSGarrett D'Amore */ 59995c635efSGarrett D'Amore 600*698f87a4SGarrett D'Amore if (TERMFONT_NONE == term_fonttop(p)) { 601*698f87a4SGarrett D'Amore if (p->col + sz >= p->maxcols) 602*698f87a4SGarrett D'Amore adjbuf(p, p->col + sz); 603*698f87a4SGarrett D'Amore for (i = 0; i < sz; i++) 60495c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 60595c635efSGarrett D'Amore return; 60695c635efSGarrett D'Amore } 60795c635efSGarrett D'Amore 60895c635efSGarrett D'Amore /* Pre-buffer, assuming worst-case. */ 60995c635efSGarrett D'Amore 610*698f87a4SGarrett D'Amore if (p->col + 1 + (sz * 5) >= p->maxcols) 611*698f87a4SGarrett D'Amore adjbuf(p, p->col + 1 + (sz * 5)); 61295c635efSGarrett D'Amore 613*698f87a4SGarrett D'Amore for (i = 0; i < sz; i++) { 614*698f87a4SGarrett D'Amore if (ASCII_HYPH == word[i] || 615*698f87a4SGarrett D'Amore isgraph((unsigned char)word[i])) 616*698f87a4SGarrett D'Amore encode1(p, word[i]); 61795c635efSGarrett D'Amore else 61895c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 61995c635efSGarrett D'Amore } 62095c635efSGarrett D'Amore } 62195c635efSGarrett D'Amore 62295c635efSGarrett D'Amore size_t 62395c635efSGarrett D'Amore term_len(const struct termp *p, size_t sz) 62495c635efSGarrett D'Amore { 62595c635efSGarrett D'Amore 62695c635efSGarrett D'Amore return((*p->width)(p, ' ') * sz); 62795c635efSGarrett D'Amore } 62895c635efSGarrett D'Amore 629*698f87a4SGarrett D'Amore static size_t 630*698f87a4SGarrett D'Amore cond_width(const struct termp *p, int c, int *skip) 631*698f87a4SGarrett D'Amore { 632*698f87a4SGarrett D'Amore 633*698f87a4SGarrett D'Amore if (*skip) { 634*698f87a4SGarrett D'Amore (*skip) = 0; 635*698f87a4SGarrett D'Amore return(0); 636*698f87a4SGarrett D'Amore } else 637*698f87a4SGarrett D'Amore return((*p->width)(p, c)); 638*698f87a4SGarrett D'Amore } 63995c635efSGarrett D'Amore 64095c635efSGarrett D'Amore size_t 64195c635efSGarrett D'Amore term_strlen(const struct termp *p, const char *cp) 64295c635efSGarrett D'Amore { 64395c635efSGarrett D'Amore size_t sz, rsz, i; 644*698f87a4SGarrett D'Amore int ssz, skip, c; 64595c635efSGarrett D'Amore const char *seq, *rhs; 64695c635efSGarrett D'Amore enum mandoc_esc esc; 64795c635efSGarrett D'Amore static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' }; 64895c635efSGarrett D'Amore 64995c635efSGarrett D'Amore /* 65095c635efSGarrett D'Amore * Account for escaped sequences within string length 65195c635efSGarrett D'Amore * calculations. This follows the logic in term_word() as we 65295c635efSGarrett D'Amore * must calculate the width of produced strings. 65395c635efSGarrett D'Amore */ 65495c635efSGarrett D'Amore 65595c635efSGarrett D'Amore sz = 0; 656*698f87a4SGarrett D'Amore skip = 0; 65795c635efSGarrett D'Amore while ('\0' != *cp) { 65895c635efSGarrett D'Amore rsz = strcspn(cp, rej); 65995c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 660*698f87a4SGarrett D'Amore sz += cond_width(p, *cp++, &skip); 66195c635efSGarrett D'Amore 66295c635efSGarrett D'Amore c = 0; 66395c635efSGarrett D'Amore switch (*cp) { 66495c635efSGarrett D'Amore case ('\\'): 66595c635efSGarrett D'Amore cp++; 66695c635efSGarrett D'Amore esc = mandoc_escape(&cp, &seq, &ssz); 66795c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 66895c635efSGarrett D'Amore return(sz); 66995c635efSGarrett D'Amore 67095c635efSGarrett D'Amore if (TERMENC_ASCII != p->enc) 67195c635efSGarrett D'Amore switch (esc) { 67295c635efSGarrett D'Amore case (ESCAPE_UNICODE): 67395c635efSGarrett D'Amore c = mchars_num2uc 67495c635efSGarrett D'Amore (seq + 1, ssz - 1); 67595c635efSGarrett D'Amore if ('\0' == c) 67695c635efSGarrett D'Amore break; 677*698f87a4SGarrett D'Amore sz += cond_width(p, c, &skip); 67895c635efSGarrett D'Amore continue; 67995c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 68095c635efSGarrett D'Amore c = mchars_spec2cp 68195c635efSGarrett D'Amore (p->symtab, seq, ssz); 68295c635efSGarrett D'Amore if (c <= 0) 68395c635efSGarrett D'Amore break; 684*698f87a4SGarrett D'Amore sz += cond_width(p, c, &skip); 68595c635efSGarrett D'Amore continue; 68695c635efSGarrett D'Amore default: 68795c635efSGarrett D'Amore break; 68895c635efSGarrett D'Amore } 68995c635efSGarrett D'Amore 69095c635efSGarrett D'Amore rhs = NULL; 69195c635efSGarrett D'Amore 69295c635efSGarrett D'Amore switch (esc) { 69395c635efSGarrett D'Amore case (ESCAPE_UNICODE): 694*698f87a4SGarrett D'Amore sz += cond_width(p, '?', &skip); 69595c635efSGarrett D'Amore break; 69695c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 69795c635efSGarrett D'Amore c = mchars_num2char(seq, ssz); 69895c635efSGarrett D'Amore if ('\0' != c) 699*698f87a4SGarrett D'Amore sz += cond_width(p, c, &skip); 70095c635efSGarrett D'Amore break; 70195c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 70295c635efSGarrett D'Amore rhs = mchars_spec2str 70395c635efSGarrett D'Amore (p->symtab, seq, ssz, &rsz); 70495c635efSGarrett D'Amore 70595c635efSGarrett D'Amore if (ssz != 1 || rhs) 70695c635efSGarrett D'Amore break; 70795c635efSGarrett D'Amore 70895c635efSGarrett D'Amore rhs = seq; 70995c635efSGarrett D'Amore rsz = ssz; 71095c635efSGarrett D'Amore break; 711*698f87a4SGarrett D'Amore case (ESCAPE_SKIPCHAR): 712*698f87a4SGarrett D'Amore skip = 1; 713*698f87a4SGarrett D'Amore break; 71495c635efSGarrett D'Amore default: 71595c635efSGarrett D'Amore break; 71695c635efSGarrett D'Amore } 71795c635efSGarrett D'Amore 71895c635efSGarrett D'Amore if (NULL == rhs) 71995c635efSGarrett D'Amore break; 72095c635efSGarrett D'Amore 721*698f87a4SGarrett D'Amore if (skip) { 722*698f87a4SGarrett D'Amore skip = 0; 723*698f87a4SGarrett D'Amore break; 724*698f87a4SGarrett D'Amore } 725*698f87a4SGarrett D'Amore 72695c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 72795c635efSGarrett D'Amore sz += (*p->width)(p, *rhs++); 72895c635efSGarrett D'Amore break; 72995c635efSGarrett D'Amore case (ASCII_NBRSP): 730*698f87a4SGarrett D'Amore sz += cond_width(p, ' ', &skip); 73195c635efSGarrett D'Amore cp++; 73295c635efSGarrett D'Amore break; 73395c635efSGarrett D'Amore case (ASCII_HYPH): 734*698f87a4SGarrett D'Amore sz += cond_width(p, '-', &skip); 73595c635efSGarrett D'Amore cp++; 73695c635efSGarrett D'Amore break; 73795c635efSGarrett D'Amore default: 73895c635efSGarrett D'Amore break; 73995c635efSGarrett D'Amore } 74095c635efSGarrett D'Amore } 74195c635efSGarrett D'Amore 74295c635efSGarrett D'Amore return(sz); 74395c635efSGarrett D'Amore } 74495c635efSGarrett D'Amore 74595c635efSGarrett D'Amore /* ARGSUSED */ 74695c635efSGarrett D'Amore size_t 74795c635efSGarrett D'Amore term_vspan(const struct termp *p, const struct roffsu *su) 74895c635efSGarrett D'Amore { 74995c635efSGarrett D'Amore double r; 75095c635efSGarrett D'Amore 75195c635efSGarrett D'Amore switch (su->unit) { 75295c635efSGarrett D'Amore case (SCALE_CM): 75395c635efSGarrett D'Amore r = su->scale * 2; 75495c635efSGarrett D'Amore break; 75595c635efSGarrett D'Amore case (SCALE_IN): 75695c635efSGarrett D'Amore r = su->scale * 6; 75795c635efSGarrett D'Amore break; 75895c635efSGarrett D'Amore case (SCALE_PC): 75995c635efSGarrett D'Amore r = su->scale; 76095c635efSGarrett D'Amore break; 76195c635efSGarrett D'Amore case (SCALE_PT): 76295c635efSGarrett D'Amore r = su->scale / 8; 76395c635efSGarrett D'Amore break; 76495c635efSGarrett D'Amore case (SCALE_MM): 76595c635efSGarrett D'Amore r = su->scale / 1000; 76695c635efSGarrett D'Amore break; 76795c635efSGarrett D'Amore case (SCALE_VS): 76895c635efSGarrett D'Amore r = su->scale; 76995c635efSGarrett D'Amore break; 77095c635efSGarrett D'Amore default: 77195c635efSGarrett D'Amore r = su->scale - 1; 77295c635efSGarrett D'Amore break; 77395c635efSGarrett D'Amore } 77495c635efSGarrett D'Amore 77595c635efSGarrett D'Amore if (r < 0.0) 77695c635efSGarrett D'Amore r = 0.0; 77795c635efSGarrett D'Amore return(/* LINTED */(size_t) 77895c635efSGarrett D'Amore r); 77995c635efSGarrett D'Amore } 78095c635efSGarrett D'Amore 78195c635efSGarrett D'Amore size_t 78295c635efSGarrett D'Amore term_hspan(const struct termp *p, const struct roffsu *su) 78395c635efSGarrett D'Amore { 78495c635efSGarrett D'Amore double v; 78595c635efSGarrett D'Amore 78695c635efSGarrett D'Amore v = ((*p->hspan)(p, su)); 78795c635efSGarrett D'Amore if (v < 0.0) 78895c635efSGarrett D'Amore v = 0.0; 78995c635efSGarrett D'Amore return((size_t) /* LINTED */ 79095c635efSGarrett D'Amore v); 79195c635efSGarrett D'Amore } 792