1*c66b8046SYuri Pankov /* $Id: term.c,v 1.274 2017/07/28 14:25:48 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 395c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4a40ea1a7SYuri Pankov * Copyright (c) 2010-2017 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 * 10371584c2SYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12371584c2SYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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" 29260e9a87SYuri 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 *); 35*c66b8046SYuri Pankov static void adjbuf(struct termp_col *, 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); 39*c66b8046SYuri Pankov static void endline(struct termp *); 4095c635efSGarrett D'Amore 41260e9a87SYuri Pankov 4295c635efSGarrett D'Amore void 43*c66b8046SYuri Pankov term_setcol(struct termp *p, size_t maxtcol) 44*c66b8046SYuri Pankov { 45*c66b8046SYuri Pankov if (maxtcol > p->maxtcol) { 46*c66b8046SYuri Pankov p->tcols = mandoc_recallocarray(p->tcols, 47*c66b8046SYuri Pankov p->maxtcol, maxtcol, sizeof(*p->tcols)); 48*c66b8046SYuri Pankov p->maxtcol = maxtcol; 49*c66b8046SYuri Pankov } 50*c66b8046SYuri Pankov p->lasttcol = maxtcol - 1; 51*c66b8046SYuri Pankov p->tcol = p->tcols; 52*c66b8046SYuri Pankov } 53*c66b8046SYuri Pankov 54*c66b8046SYuri Pankov void 5595c635efSGarrett D'Amore term_free(struct termp *p) 5695c635efSGarrett D'Amore { 57*c66b8046SYuri Pankov for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++) 58*c66b8046SYuri Pankov free(p->tcol->buf); 59*c66b8046SYuri Pankov free(p->tcols); 60260e9a87SYuri Pankov free(p->fontq); 6195c635efSGarrett D'Amore free(p); 6295c635efSGarrett D'Amore } 6395c635efSGarrett D'Amore 6495c635efSGarrett D'Amore void 6595c635efSGarrett D'Amore term_begin(struct termp *p, term_margin head, 66371584c2SYuri Pankov term_margin foot, const struct roff_meta *arg) 6795c635efSGarrett D'Amore { 6895c635efSGarrett D'Amore 6995c635efSGarrett D'Amore p->headf = head; 7095c635efSGarrett D'Amore p->footf = foot; 7195c635efSGarrett D'Amore p->argf = arg; 7295c635efSGarrett D'Amore (*p->begin)(p); 7395c635efSGarrett D'Amore } 7495c635efSGarrett D'Amore 7595c635efSGarrett D'Amore void 7695c635efSGarrett D'Amore term_end(struct termp *p) 7795c635efSGarrett D'Amore { 7895c635efSGarrett D'Amore 7995c635efSGarrett D'Amore (*p->end)(p); 8095c635efSGarrett D'Amore } 8195c635efSGarrett D'Amore 8295c635efSGarrett D'Amore /* 83260e9a87SYuri Pankov * Flush a chunk of text. By default, break the output line each time 84260e9a87SYuri Pankov * the right margin is reached, and continue output on the next line 85260e9a87SYuri Pankov * at the same offset as the chunk itself. By default, also break the 86260e9a87SYuri Pankov * output line at the end of the chunk. 8795c635efSGarrett D'Amore * The following flags may be specified: 8895c635efSGarrett D'Amore * 89260e9a87SYuri Pankov * - TERMP_NOBREAK: Do not break the output line at the right margin, 90260e9a87SYuri Pankov * but only at the max right margin. Also, do not break the output 91260e9a87SYuri Pankov * line at the end of the chunk, such that the next call can pad to 92260e9a87SYuri Pankov * the next column. However, if less than p->trailspace blanks, 93260e9a87SYuri Pankov * which can be 0, 1, or 2, remain to the right margin, the line 94260e9a87SYuri Pankov * will be broken. 95371584c2SYuri Pankov * - TERMP_BRTRSP: Consider trailing whitespace significant 96371584c2SYuri Pankov * when deciding whether the chunk fits or not. 97260e9a87SYuri Pankov * - TERMP_BRIND: If the chunk does not fit and the output line has 98260e9a87SYuri Pankov * to be broken, start the next line at the right margin instead 99260e9a87SYuri Pankov * of at the offset. Used together with TERMP_NOBREAK for the tags 100260e9a87SYuri Pankov * in various kinds of tagged lists. 101*c66b8046SYuri Pankov * - TERMP_HANG: Do not break the output line at the right margin, 102260e9a87SYuri Pankov * append the next chunk after it even if this one is too long. 103260e9a87SYuri Pankov * To be used together with TERMP_NOBREAK. 104*c66b8046SYuri Pankov * - TERMP_NOPAD: Start writing at the current position, 105*c66b8046SYuri Pankov * do not pad with blank characters up to the offset. 10695c635efSGarrett D'Amore */ 10795c635efSGarrett D'Amore void 10895c635efSGarrett D'Amore term_flushln(struct termp *p) 10995c635efSGarrett D'Amore { 11095c635efSGarrett D'Amore size_t vis; /* current visual position on output */ 11195c635efSGarrett D'Amore size_t vbl; /* number of blanks to prepend to output */ 11295c635efSGarrett D'Amore size_t vend; /* end of word visual position on output */ 11395c635efSGarrett D'Amore size_t bp; /* visual right border position */ 11495c635efSGarrett D'Amore size_t dv; /* temporary for visual pos calculations */ 115*c66b8046SYuri Pankov size_t j; /* temporary loop index for p->tcol->buf */ 116698f87a4SGarrett D'Amore size_t jhy; /* last hyph before overflow w/r/t j */ 11795c635efSGarrett D'Amore size_t maxvis; /* output position of visible boundary */ 118*c66b8046SYuri Pankov int ntab; /* number of tabs to prepend */ 119*c66b8046SYuri Pankov int breakline; /* after this word */ 12095c635efSGarrett D'Amore 121*c66b8046SYuri Pankov vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ? 122*c66b8046SYuri Pankov 0 : p->tcol->offset - p->viscol; 123*c66b8046SYuri Pankov if (p->minbl && vbl < p->minbl) 124*c66b8046SYuri Pankov vbl = p->minbl; 125*c66b8046SYuri Pankov maxvis = p->tcol->rmargin > p->viscol + vbl ? 126*c66b8046SYuri Pankov p->tcol->rmargin - p->viscol - vbl : 0; 127*c66b8046SYuri Pankov bp = !(p->flags & TERMP_NOBREAK) ? maxvis : 128*c66b8046SYuri Pankov p->maxrmargin > p->viscol + vbl ? 129*c66b8046SYuri Pankov p->maxrmargin - p->viscol - vbl : 0; 13095c635efSGarrett D'Amore vis = vend = 0; 13195c635efSGarrett D'Amore 132*c66b8046SYuri Pankov if ((p->flags & TERMP_MULTICOL) == 0) 133*c66b8046SYuri Pankov p->tcol->col = 0; 134*c66b8046SYuri Pankov while (p->tcol->col < p->tcol->lastcol) { 135*c66b8046SYuri Pankov 13695c635efSGarrett D'Amore /* 13795c635efSGarrett D'Amore * Handle literal tab characters: collapse all 13895c635efSGarrett D'Amore * subsequent tabs into a single huge set of spaces. 13995c635efSGarrett D'Amore */ 140*c66b8046SYuri Pankov 141698f87a4SGarrett D'Amore ntab = 0; 142*c66b8046SYuri Pankov while (p->tcol->col < p->tcol->lastcol && 143*c66b8046SYuri Pankov p->tcol->buf[p->tcol->col] == '\t') { 144*c66b8046SYuri Pankov vend = term_tab_next(vis); 14595c635efSGarrett D'Amore vbl += vend - vis; 14695c635efSGarrett D'Amore vis = vend; 147698f87a4SGarrett D'Amore ntab++; 148*c66b8046SYuri Pankov p->tcol->col++; 14995c635efSGarrett D'Amore } 15095c635efSGarrett D'Amore 15195c635efSGarrett D'Amore /* 15295c635efSGarrett D'Amore * Count up visible word characters. Control sequences 15395c635efSGarrett D'Amore * (starting with the CSI) aren't counted. A space 15495c635efSGarrett D'Amore * generates a non-printing word, which is valid (the 15595c635efSGarrett D'Amore * space is printed according to regular spacing rules). 15695c635efSGarrett D'Amore */ 15795c635efSGarrett D'Amore 158*c66b8046SYuri Pankov jhy = 0; 159*c66b8046SYuri Pankov breakline = 0; 160*c66b8046SYuri Pankov for (j = p->tcol->col; j < p->tcol->lastcol; j++) { 161*c66b8046SYuri Pankov if (p->tcol->buf[j] == '\n') { 162*c66b8046SYuri Pankov if ((p->flags & TERMP_BRIND) == 0) 163*c66b8046SYuri Pankov breakline = 1; 164*c66b8046SYuri Pankov continue; 165*c66b8046SYuri Pankov } 166*c66b8046SYuri Pankov if (p->tcol->buf[j] == ' ' || p->tcol->buf[j] == '\t') 16795c635efSGarrett D'Amore break; 16895c635efSGarrett D'Amore 169371584c2SYuri Pankov /* Back over the last printed character. */ 170*c66b8046SYuri Pankov if (p->tcol->buf[j] == '\b') { 17195c635efSGarrett D'Amore assert(j); 172*c66b8046SYuri Pankov vend -= (*p->width)(p, p->tcol->buf[j - 1]); 17395c635efSGarrett D'Amore continue; 17495c635efSGarrett D'Amore } 17595c635efSGarrett D'Amore 17695c635efSGarrett D'Amore /* Regular word. */ 17795c635efSGarrett D'Amore /* Break at the hyphen point if we overrun. */ 17895c635efSGarrett D'Amore if (vend > vis && vend < bp && 179*c66b8046SYuri Pankov (p->tcol->buf[j] == ASCII_HYPH|| 180*c66b8046SYuri Pankov p->tcol->buf[j] == ASCII_BREAK)) 18195c635efSGarrett D'Amore jhy = j; 18295c635efSGarrett D'Amore 183260e9a87SYuri Pankov /* 184260e9a87SYuri Pankov * Hyphenation now decided, put back a real 185260e9a87SYuri Pankov * hyphen such that we get the correct width. 186260e9a87SYuri Pankov */ 187*c66b8046SYuri Pankov if (p->tcol->buf[j] == ASCII_HYPH) 188*c66b8046SYuri Pankov p->tcol->buf[j] = '-'; 189260e9a87SYuri Pankov 190*c66b8046SYuri Pankov vend += (*p->width)(p, p->tcol->buf[j]); 19195c635efSGarrett D'Amore } 19295c635efSGarrett D'Amore 19395c635efSGarrett D'Amore /* 19495c635efSGarrett D'Amore * Find out whether we would exceed the right margin. 19595c635efSGarrett D'Amore * If so, break to the next line. 19695c635efSGarrett D'Amore */ 197*c66b8046SYuri Pankov 198*c66b8046SYuri Pankov if (vend > bp && jhy == 0 && vis > 0 && 199*c66b8046SYuri Pankov (p->flags & TERMP_BRNEVER) == 0) { 200*c66b8046SYuri Pankov if (p->flags & TERMP_MULTICOL) 201*c66b8046SYuri Pankov return; 202*c66b8046SYuri Pankov 203*c66b8046SYuri Pankov endline(p); 20495c635efSGarrett D'Amore vend -= vis; 20595c635efSGarrett D'Amore 206*c66b8046SYuri Pankov /* Use pending tabs on the new line. */ 207698f87a4SGarrett D'Amore 208*c66b8046SYuri Pankov vbl = 0; 209*c66b8046SYuri Pankov while (ntab--) 210*c66b8046SYuri Pankov vbl = term_tab_next(vbl); 211698f87a4SGarrett D'Amore 212*c66b8046SYuri Pankov /* Re-establish indentation. */ 21395c635efSGarrett D'Amore 214*c66b8046SYuri Pankov if (p->flags & TERMP_BRIND) 215*c66b8046SYuri Pankov vbl += p->tcol->rmargin; 216*c66b8046SYuri Pankov else 217*c66b8046SYuri Pankov vbl += p->tcol->offset; 218*c66b8046SYuri Pankov maxvis = p->tcol->rmargin > vbl ? 219*c66b8046SYuri Pankov p->tcol->rmargin - vbl : 0; 220*c66b8046SYuri Pankov bp = !(p->flags & TERMP_NOBREAK) ? maxvis : 221*c66b8046SYuri Pankov p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; 22295c635efSGarrett D'Amore } 22395c635efSGarrett D'Amore 224*c66b8046SYuri Pankov /* 225*c66b8046SYuri Pankov * Write out the rest of the word. 226*c66b8046SYuri Pankov */ 227*c66b8046SYuri Pankov 228*c66b8046SYuri Pankov for ( ; p->tcol->col < p->tcol->lastcol; p->tcol->col++) { 229*c66b8046SYuri Pankov if (vend > bp && jhy > 0 && p->tcol->col > jhy) 23095c635efSGarrett D'Amore break; 231*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == '\n') 232*c66b8046SYuri Pankov continue; 233*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == '\t') 23495c635efSGarrett D'Amore break; 235*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == ' ') { 236*c66b8046SYuri Pankov j = p->tcol->col; 237*c66b8046SYuri Pankov while (p->tcol->col < p->tcol->lastcol && 238*c66b8046SYuri Pankov p->tcol->buf[p->tcol->col] == ' ') 239*c66b8046SYuri Pankov p->tcol->col++; 240*c66b8046SYuri Pankov dv = (p->tcol->col - j) * (*p->width)(p, ' '); 24195c635efSGarrett D'Amore vbl += dv; 24295c635efSGarrett D'Amore vend += dv; 24395c635efSGarrett D'Amore break; 24495c635efSGarrett D'Amore } 245*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) { 24695c635efSGarrett D'Amore vbl += (*p->width)(p, ' '); 24795c635efSGarrett D'Amore continue; 24895c635efSGarrett D'Amore } 249*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == ASCII_BREAK) 250260e9a87SYuri Pankov continue; 25195c635efSGarrett D'Amore 25295c635efSGarrett D'Amore /* 25395c635efSGarrett D'Amore * Now we definitely know there will be 25495c635efSGarrett D'Amore * printable characters to output, 25595c635efSGarrett D'Amore * so write preceding white space now. 25695c635efSGarrett D'Amore */ 25795c635efSGarrett D'Amore if (vbl) { 25895c635efSGarrett D'Amore (*p->advance)(p, vbl); 25995c635efSGarrett D'Amore p->viscol += vbl; 26095c635efSGarrett D'Amore vbl = 0; 26195c635efSGarrett D'Amore } 26295c635efSGarrett D'Amore 263*c66b8046SYuri Pankov (*p->letter)(p, p->tcol->buf[p->tcol->col]); 264*c66b8046SYuri Pankov if (p->tcol->buf[p->tcol->col] == '\b') 265*c66b8046SYuri Pankov p->viscol -= (*p->width)(p, 266*c66b8046SYuri Pankov p->tcol->buf[p->tcol->col - 1]); 26795c635efSGarrett D'Amore else 268*c66b8046SYuri Pankov p->viscol += (*p->width)(p, 269*c66b8046SYuri Pankov p->tcol->buf[p->tcol->col]); 27095c635efSGarrett D'Amore } 27195c635efSGarrett D'Amore vis = vend; 272*c66b8046SYuri Pankov 273*c66b8046SYuri Pankov if (breakline == 0) 274*c66b8046SYuri Pankov continue; 275*c66b8046SYuri Pankov 276*c66b8046SYuri Pankov /* Explicitly requested output line break. */ 277*c66b8046SYuri Pankov 278*c66b8046SYuri Pankov if (p->flags & TERMP_MULTICOL) 279*c66b8046SYuri Pankov return; 280*c66b8046SYuri Pankov 281*c66b8046SYuri Pankov endline(p); 282*c66b8046SYuri Pankov breakline = 0; 283*c66b8046SYuri Pankov vis = vend = 0; 284*c66b8046SYuri Pankov 285*c66b8046SYuri Pankov /* Re-establish indentation. */ 286*c66b8046SYuri Pankov 287*c66b8046SYuri Pankov vbl = p->tcol->offset; 288*c66b8046SYuri Pankov maxvis = p->tcol->rmargin > vbl ? 289*c66b8046SYuri Pankov p->tcol->rmargin - vbl : 0; 290*c66b8046SYuri Pankov bp = !(p->flags & TERMP_NOBREAK) ? maxvis : 291*c66b8046SYuri Pankov p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; 29295c635efSGarrett D'Amore } 29395c635efSGarrett D'Amore 29495c635efSGarrett D'Amore /* 29595c635efSGarrett D'Amore * If there was trailing white space, it was not printed; 29695c635efSGarrett D'Amore * so reset the cursor position accordingly. 29795c635efSGarrett D'Amore */ 298*c66b8046SYuri Pankov 299260e9a87SYuri Pankov if (vis > vbl) 30095c635efSGarrett D'Amore vis -= vbl; 301260e9a87SYuri Pankov else 302260e9a87SYuri Pankov vis = 0; 30395c635efSGarrett D'Amore 304*c66b8046SYuri Pankov p->col = p->tcol->col = p->tcol->lastcol = 0; 305*c66b8046SYuri Pankov p->minbl = p->trailspace; 306*c66b8046SYuri Pankov p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); 30795c635efSGarrett D'Amore 308*c66b8046SYuri Pankov if (p->flags & TERMP_MULTICOL) 30995c635efSGarrett D'Amore return; 31095c635efSGarrett D'Amore 311371584c2SYuri Pankov /* Trailing whitespace is significant in some columns. */ 312*c66b8046SYuri Pankov 313371584c2SYuri Pankov if (vis && vbl && (TERMP_BRTRSP & p->flags)) 314371584c2SYuri Pankov vis += vbl; 315371584c2SYuri Pankov 31695c635efSGarrett D'Amore /* If the column was overrun, break the line. */ 317*c66b8046SYuri Pankov if ((p->flags & TERMP_NOBREAK) == 0 || 318*c66b8046SYuri Pankov ((p->flags & TERMP_HANG) == 0 && 319*c66b8046SYuri Pankov vis + p->trailspace * (*p->width)(p, ' ') > maxvis)) 320*c66b8046SYuri Pankov endline(p); 32195c635efSGarrett D'Amore } 322*c66b8046SYuri Pankov 323*c66b8046SYuri Pankov static void 324*c66b8046SYuri Pankov endline(struct termp *p) 325*c66b8046SYuri Pankov { 326*c66b8046SYuri Pankov if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) { 327*c66b8046SYuri Pankov p->mc = NULL; 328*c66b8046SYuri Pankov p->flags &= ~TERMP_ENDMC; 329*c66b8046SYuri Pankov } 330*c66b8046SYuri Pankov if (p->mc != NULL) { 331*c66b8046SYuri Pankov if (p->viscol && p->maxrmargin >= p->viscol) 332*c66b8046SYuri Pankov (*p->advance)(p, p->maxrmargin - p->viscol + 1); 333*c66b8046SYuri Pankov p->flags |= TERMP_NOBUF | TERMP_NOSPACE; 334*c66b8046SYuri Pankov term_word(p, p->mc); 335*c66b8046SYuri Pankov p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC); 336*c66b8046SYuri Pankov } 337*c66b8046SYuri Pankov p->viscol = 0; 338*c66b8046SYuri Pankov p->minbl = 0; 339*c66b8046SYuri Pankov (*p->endline)(p); 34095c635efSGarrett D'Amore } 34195c635efSGarrett D'Amore 34295c635efSGarrett D'Amore /* 34395c635efSGarrett D'Amore * A newline only breaks an existing line; it won't assert vertical 34495c635efSGarrett D'Amore * space. All data in the output buffer is flushed prior to the newline 34595c635efSGarrett D'Amore * assertion. 34695c635efSGarrett D'Amore */ 34795c635efSGarrett D'Amore void 34895c635efSGarrett D'Amore term_newln(struct termp *p) 34995c635efSGarrett D'Amore { 35095c635efSGarrett D'Amore 35195c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 352*c66b8046SYuri Pankov if (p->tcol->lastcol || p->viscol) 35395c635efSGarrett D'Amore term_flushln(p); 35495c635efSGarrett D'Amore } 35595c635efSGarrett D'Amore 35695c635efSGarrett D'Amore /* 35795c635efSGarrett D'Amore * Asserts a vertical space (a full, empty line-break between lines). 35895c635efSGarrett D'Amore * Note that if used twice, this will cause two blank spaces and so on. 35995c635efSGarrett D'Amore * All data in the output buffer is flushed prior to the newline 36095c635efSGarrett D'Amore * assertion. 36195c635efSGarrett D'Amore */ 36295c635efSGarrett D'Amore void 36395c635efSGarrett D'Amore term_vspace(struct termp *p) 36495c635efSGarrett D'Amore { 36595c635efSGarrett D'Amore 36695c635efSGarrett D'Amore term_newln(p); 36795c635efSGarrett D'Amore p->viscol = 0; 368*c66b8046SYuri Pankov p->minbl = 0; 369698f87a4SGarrett D'Amore if (0 < p->skipvsp) 370698f87a4SGarrett D'Amore p->skipvsp--; 371698f87a4SGarrett D'Amore else 37295c635efSGarrett D'Amore (*p->endline)(p); 37395c635efSGarrett D'Amore } 37495c635efSGarrett D'Amore 375260e9a87SYuri Pankov /* Swap current and previous font; for \fP and .ft P */ 37695c635efSGarrett D'Amore void 37795c635efSGarrett D'Amore term_fontlast(struct termp *p) 37895c635efSGarrett D'Amore { 37995c635efSGarrett D'Amore enum termfont f; 38095c635efSGarrett D'Amore 38195c635efSGarrett D'Amore f = p->fontl; 38295c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 38395c635efSGarrett D'Amore p->fontq[p->fonti] = f; 38495c635efSGarrett D'Amore } 38595c635efSGarrett D'Amore 386260e9a87SYuri Pankov /* Set font, save current, discard previous; for \f, .ft, .B etc. */ 38795c635efSGarrett D'Amore void 38895c635efSGarrett D'Amore term_fontrepl(struct termp *p, enum termfont f) 38995c635efSGarrett D'Amore { 39095c635efSGarrett D'Amore 39195c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 39295c635efSGarrett D'Amore p->fontq[p->fonti] = f; 39395c635efSGarrett D'Amore } 39495c635efSGarrett D'Amore 395260e9a87SYuri Pankov /* Set font, save previous. */ 39695c635efSGarrett D'Amore void 39795c635efSGarrett D'Amore term_fontpush(struct termp *p, enum termfont f) 39895c635efSGarrett D'Amore { 39995c635efSGarrett D'Amore 40095c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 401260e9a87SYuri Pankov if (++p->fonti == p->fontsz) { 402260e9a87SYuri Pankov p->fontsz += 8; 403260e9a87SYuri Pankov p->fontq = mandoc_reallocarray(p->fontq, 404371584c2SYuri Pankov p->fontsz, sizeof(*p->fontq)); 405260e9a87SYuri Pankov } 406260e9a87SYuri Pankov p->fontq[p->fonti] = f; 40795c635efSGarrett D'Amore } 40895c635efSGarrett D'Amore 409260e9a87SYuri Pankov /* Flush to make the saved pointer current again. */ 41095c635efSGarrett D'Amore void 411260e9a87SYuri Pankov term_fontpopq(struct termp *p, int i) 41295c635efSGarrett D'Amore { 41395c635efSGarrett D'Amore 414260e9a87SYuri Pankov assert(i >= 0); 415260e9a87SYuri Pankov if (p->fonti > i) 416260e9a87SYuri Pankov p->fonti = i; 41795c635efSGarrett D'Amore } 41895c635efSGarrett D'Amore 419260e9a87SYuri Pankov /* Pop one font off the stack. */ 42095c635efSGarrett D'Amore void 42195c635efSGarrett D'Amore term_fontpop(struct termp *p) 42295c635efSGarrett D'Amore { 42395c635efSGarrett D'Amore 42495c635efSGarrett D'Amore assert(p->fonti); 42595c635efSGarrett D'Amore p->fonti--; 42695c635efSGarrett D'Amore } 42795c635efSGarrett D'Amore 42895c635efSGarrett D'Amore /* 42995c635efSGarrett D'Amore * Handle pwords, partial words, which may be either a single word or a 43095c635efSGarrett D'Amore * phrase that cannot be broken down (such as a literal string). This 43195c635efSGarrett D'Amore * handles word styling. 43295c635efSGarrett D'Amore */ 43395c635efSGarrett D'Amore void 43495c635efSGarrett D'Amore term_word(struct termp *p, const char *word) 43595c635efSGarrett D'Amore { 436*c66b8046SYuri Pankov struct roffsu su; 437698f87a4SGarrett D'Amore const char nbrsp[2] = { ASCII_NBRSP, 0 }; 43895c635efSGarrett D'Amore const char *seq, *cp; 43995c635efSGarrett D'Amore int sz, uc; 440*c66b8046SYuri Pankov size_t csz, lsz, ssz; 44195c635efSGarrett D'Amore enum mandoc_esc esc; 44295c635efSGarrett D'Amore 443*c66b8046SYuri Pankov if ((p->flags & TERMP_NOBUF) == 0) { 444*c66b8046SYuri Pankov if ((p->flags & TERMP_NOSPACE) == 0) { 445*c66b8046SYuri Pankov if ((p->flags & TERMP_KEEP) == 0) { 44695c635efSGarrett D'Amore bufferc(p, ' '); 447*c66b8046SYuri Pankov if (p->flags & TERMP_SENTENCE) 44895c635efSGarrett D'Amore bufferc(p, ' '); 44995c635efSGarrett D'Amore } else 45095c635efSGarrett D'Amore bufferc(p, ASCII_NBRSP); 45195c635efSGarrett D'Amore } 452*c66b8046SYuri Pankov if (p->flags & TERMP_PREKEEP) 453698f87a4SGarrett D'Amore p->flags |= TERMP_KEEP; 454*c66b8046SYuri Pankov if (p->flags & TERMP_NONOSPACE) 45595c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 456*c66b8046SYuri Pankov else 457*c66b8046SYuri Pankov p->flags &= ~TERMP_NOSPACE; 458260e9a87SYuri Pankov p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); 459260e9a87SYuri Pankov p->skipvsp = 0; 460*c66b8046SYuri Pankov } 46195c635efSGarrett D'Amore 46295c635efSGarrett D'Amore while ('\0' != *word) { 463698f87a4SGarrett D'Amore if ('\\' != *word) { 464698f87a4SGarrett D'Amore if (TERMP_NBRWORD & p->flags) { 465698f87a4SGarrett D'Amore if (' ' == *word) { 466698f87a4SGarrett D'Amore encode(p, nbrsp, 1); 467698f87a4SGarrett D'Amore word++; 468698f87a4SGarrett D'Amore continue; 469698f87a4SGarrett D'Amore } 470698f87a4SGarrett D'Amore ssz = strcspn(word, "\\ "); 471698f87a4SGarrett D'Amore } else 472698f87a4SGarrett D'Amore ssz = strcspn(word, "\\"); 473698f87a4SGarrett D'Amore encode(p, word, ssz); 474698f87a4SGarrett D'Amore word += (int)ssz; 475698f87a4SGarrett D'Amore continue; 476698f87a4SGarrett D'Amore } 47795c635efSGarrett D'Amore 47895c635efSGarrett D'Amore word++; 47995c635efSGarrett D'Amore esc = mandoc_escape(&word, &seq, &sz); 48095c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 481260e9a87SYuri Pankov continue; 48295c635efSGarrett D'Amore 48395c635efSGarrett D'Amore switch (esc) { 484260e9a87SYuri Pankov case ESCAPE_UNICODE: 48595c635efSGarrett D'Amore uc = mchars_num2uc(seq + 1, sz - 1); 48695c635efSGarrett D'Amore break; 487260e9a87SYuri Pankov case ESCAPE_NUMBERED: 488260e9a87SYuri Pankov uc = mchars_num2char(seq, sz); 489260e9a87SYuri Pankov if (uc < 0) 49095c635efSGarrett D'Amore continue; 49195c635efSGarrett D'Amore break; 492260e9a87SYuri Pankov case ESCAPE_SPECIAL: 493260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 494371584c2SYuri Pankov cp = mchars_spec2str(seq, sz, &ssz); 495260e9a87SYuri Pankov if (cp != NULL) 49695c635efSGarrett D'Amore encode(p, cp, ssz); 497260e9a87SYuri Pankov } else { 498371584c2SYuri Pankov uc = mchars_spec2cp(seq, sz); 499260e9a87SYuri Pankov if (uc > 0) 500260e9a87SYuri Pankov encode1(p, uc); 501260e9a87SYuri Pankov } 502260e9a87SYuri Pankov continue; 503260e9a87SYuri Pankov case ESCAPE_FONTBOLD: 50495c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_BOLD); 505260e9a87SYuri Pankov continue; 506260e9a87SYuri Pankov case ESCAPE_FONTITALIC: 50795c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_UNDER); 508260e9a87SYuri Pankov continue; 509260e9a87SYuri Pankov case ESCAPE_FONTBI: 510698f87a4SGarrett D'Amore term_fontrepl(p, TERMFONT_BI); 511260e9a87SYuri Pankov continue; 512260e9a87SYuri Pankov case ESCAPE_FONT: 513260e9a87SYuri Pankov case ESCAPE_FONTROMAN: 51495c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_NONE); 515260e9a87SYuri Pankov continue; 516260e9a87SYuri Pankov case ESCAPE_FONTPREV: 51795c635efSGarrett D'Amore term_fontlast(p); 518260e9a87SYuri Pankov continue; 519*c66b8046SYuri Pankov case ESCAPE_BREAK: 520*c66b8046SYuri Pankov bufferc(p, '\n'); 521*c66b8046SYuri Pankov continue; 522260e9a87SYuri Pankov case ESCAPE_NOSPACE: 523371584c2SYuri Pankov if (p->flags & TERMP_BACKAFTER) 524371584c2SYuri Pankov p->flags &= ~TERMP_BACKAFTER; 525371584c2SYuri Pankov else if (*word == '\0') 526260e9a87SYuri Pankov p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); 527260e9a87SYuri Pankov continue; 528*c66b8046SYuri Pankov case ESCAPE_HORIZ: 529*c66b8046SYuri Pankov if (*seq == '|') { 530*c66b8046SYuri Pankov seq++; 531*c66b8046SYuri Pankov uc = -p->col; 532*c66b8046SYuri Pankov } else 533*c66b8046SYuri Pankov uc = 0; 534*c66b8046SYuri Pankov if (a2roffsu(seq, &su, SCALE_EM) == NULL) 535*c66b8046SYuri Pankov continue; 536*c66b8046SYuri Pankov uc += term_hen(p, &su); 537*c66b8046SYuri Pankov if (uc > 0) 538*c66b8046SYuri Pankov while (uc-- > 0) 539*c66b8046SYuri Pankov bufferc(p, ASCII_NBRSP); 540*c66b8046SYuri Pankov else if (p->col > (size_t)(-uc)) 541*c66b8046SYuri Pankov p->col += uc; 542*c66b8046SYuri Pankov else { 543*c66b8046SYuri Pankov uc += p->col; 544*c66b8046SYuri Pankov p->col = 0; 545*c66b8046SYuri Pankov if (p->tcol->offset > (size_t)(-uc)) { 546*c66b8046SYuri Pankov p->ti += uc; 547*c66b8046SYuri Pankov p->tcol->offset += uc; 548*c66b8046SYuri Pankov } else { 549*c66b8046SYuri Pankov p->ti -= p->tcol->offset; 550*c66b8046SYuri Pankov p->tcol->offset = 0; 551*c66b8046SYuri Pankov } 552*c66b8046SYuri Pankov } 553*c66b8046SYuri Pankov continue; 554*c66b8046SYuri Pankov case ESCAPE_HLINE: 555*c66b8046SYuri Pankov if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL) 556*c66b8046SYuri Pankov continue; 557*c66b8046SYuri Pankov uc = term_hen(p, &su); 558*c66b8046SYuri Pankov if (uc <= 0) { 559*c66b8046SYuri Pankov if (p->tcol->rmargin <= p->tcol->offset) 560*c66b8046SYuri Pankov continue; 561*c66b8046SYuri Pankov lsz = p->tcol->rmargin - p->tcol->offset; 562*c66b8046SYuri Pankov } else 563*c66b8046SYuri Pankov lsz = uc; 564*c66b8046SYuri Pankov if (*cp == seq[-1]) 565*c66b8046SYuri Pankov uc = -1; 566*c66b8046SYuri Pankov else if (*cp == '\\') { 567*c66b8046SYuri Pankov seq = cp + 1; 568*c66b8046SYuri Pankov esc = mandoc_escape(&seq, &cp, &sz); 569*c66b8046SYuri Pankov switch (esc) { 570*c66b8046SYuri Pankov case ESCAPE_UNICODE: 571*c66b8046SYuri Pankov uc = mchars_num2uc(cp + 1, sz - 1); 572*c66b8046SYuri Pankov break; 573*c66b8046SYuri Pankov case ESCAPE_NUMBERED: 574*c66b8046SYuri Pankov uc = mchars_num2char(cp, sz); 575*c66b8046SYuri Pankov break; 576*c66b8046SYuri Pankov case ESCAPE_SPECIAL: 577*c66b8046SYuri Pankov uc = mchars_spec2cp(cp, sz); 578*c66b8046SYuri Pankov break; 579*c66b8046SYuri Pankov default: 580*c66b8046SYuri Pankov uc = -1; 581*c66b8046SYuri Pankov break; 582*c66b8046SYuri Pankov } 583*c66b8046SYuri Pankov } else 584*c66b8046SYuri Pankov uc = *cp; 585*c66b8046SYuri Pankov if (uc < 0x20 || (uc > 0x7E && uc < 0xA0)) 586*c66b8046SYuri Pankov uc = '_'; 587*c66b8046SYuri Pankov if (p->enc == TERMENC_ASCII) { 588*c66b8046SYuri Pankov cp = ascii_uc2str(uc); 589*c66b8046SYuri Pankov csz = term_strlen(p, cp); 590*c66b8046SYuri Pankov ssz = strlen(cp); 591*c66b8046SYuri Pankov } else 592*c66b8046SYuri Pankov csz = (*p->width)(p, uc); 593*c66b8046SYuri Pankov while (lsz >= csz) { 594*c66b8046SYuri Pankov if (p->enc == TERMENC_ASCII) 595*c66b8046SYuri Pankov encode(p, cp, ssz); 596*c66b8046SYuri Pankov else 597*c66b8046SYuri Pankov encode1(p, uc); 598*c66b8046SYuri Pankov lsz -= csz; 599*c66b8046SYuri Pankov } 600*c66b8046SYuri Pankov continue; 601260e9a87SYuri Pankov case ESCAPE_SKIPCHAR: 602371584c2SYuri Pankov p->flags |= TERMP_BACKAFTER; 603260e9a87SYuri Pankov continue; 604260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE: 605260e9a87SYuri Pankov cp = seq + sz; 606260e9a87SYuri Pankov while (seq < cp) { 607260e9a87SYuri Pankov if (*seq == '\\') { 608260e9a87SYuri Pankov mandoc_escape(&seq, NULL, NULL); 609260e9a87SYuri Pankov continue; 610260e9a87SYuri Pankov } 611260e9a87SYuri Pankov encode1(p, *seq++); 612371584c2SYuri Pankov if (seq < cp) { 613371584c2SYuri Pankov if (p->flags & TERMP_BACKBEFORE) 614371584c2SYuri Pankov p->flags |= TERMP_BACKAFTER; 615371584c2SYuri Pankov else 616371584c2SYuri Pankov p->flags |= TERMP_BACKBEFORE; 617260e9a87SYuri Pankov } 618371584c2SYuri Pankov } 619371584c2SYuri Pankov /* Trim trailing backspace/blank pair. */ 620*c66b8046SYuri Pankov if (p->tcol->lastcol > 2 && 621*c66b8046SYuri Pankov (p->tcol->buf[p->tcol->lastcol - 1] == ' ' || 622*c66b8046SYuri Pankov p->tcol->buf[p->tcol->lastcol - 1] == '\t')) 623*c66b8046SYuri Pankov p->tcol->lastcol -= 2; 624*c66b8046SYuri Pankov if (p->col > p->tcol->lastcol) 625*c66b8046SYuri Pankov p->col = p->tcol->lastcol; 626371584c2SYuri Pankov continue; 62795c635efSGarrett D'Amore default: 628260e9a87SYuri Pankov continue; 629260e9a87SYuri Pankov } 630260e9a87SYuri Pankov 631260e9a87SYuri Pankov /* 632260e9a87SYuri Pankov * Common handling for Unicode and numbered 633260e9a87SYuri Pankov * character escape sequences. 634260e9a87SYuri Pankov */ 635260e9a87SYuri Pankov 636260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 637260e9a87SYuri Pankov cp = ascii_uc2str(uc); 638260e9a87SYuri Pankov encode(p, cp, strlen(cp)); 639260e9a87SYuri Pankov } else { 640260e9a87SYuri Pankov if ((uc < 0x20 && uc != 0x09) || 641260e9a87SYuri Pankov (uc > 0x7E && uc < 0xA0)) 642260e9a87SYuri Pankov uc = 0xFFFD; 643260e9a87SYuri Pankov encode1(p, uc); 64495c635efSGarrett D'Amore } 64595c635efSGarrett D'Amore } 646698f87a4SGarrett D'Amore p->flags &= ~TERMP_NBRWORD; 64795c635efSGarrett D'Amore } 64895c635efSGarrett D'Amore 64995c635efSGarrett D'Amore static void 650*c66b8046SYuri Pankov adjbuf(struct termp_col *c, size_t sz) 65195c635efSGarrett D'Amore { 652*c66b8046SYuri Pankov if (c->maxcols == 0) 653*c66b8046SYuri Pankov c->maxcols = 1024; 654*c66b8046SYuri Pankov while (c->maxcols <= sz) 655*c66b8046SYuri Pankov c->maxcols <<= 2; 656*c66b8046SYuri Pankov c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf)); 65795c635efSGarrett D'Amore } 65895c635efSGarrett D'Amore 65995c635efSGarrett D'Amore static void 66095c635efSGarrett D'Amore bufferc(struct termp *p, char c) 66195c635efSGarrett D'Amore { 662*c66b8046SYuri Pankov if (p->flags & TERMP_NOBUF) { 663*c66b8046SYuri Pankov (*p->letter)(p, c); 664*c66b8046SYuri Pankov return; 665*c66b8046SYuri Pankov } 666*c66b8046SYuri Pankov if (p->col + 1 >= p->tcol->maxcols) 667*c66b8046SYuri Pankov adjbuf(p->tcol, p->col + 1); 668*c66b8046SYuri Pankov if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) 669*c66b8046SYuri Pankov p->tcol->buf[p->col] = c; 670*c66b8046SYuri Pankov if (p->tcol->lastcol < ++p->col) 671*c66b8046SYuri Pankov p->tcol->lastcol = p->col; 67295c635efSGarrett D'Amore } 67395c635efSGarrett D'Amore 67495c635efSGarrett D'Amore /* 67595c635efSGarrett D'Amore * See encode(). 67695c635efSGarrett D'Amore * Do this for a single (probably unicode) value. 67795c635efSGarrett D'Amore * Does not check for non-decorated glyphs. 67895c635efSGarrett D'Amore */ 67995c635efSGarrett D'Amore static void 68095c635efSGarrett D'Amore encode1(struct termp *p, int c) 68195c635efSGarrett D'Amore { 68295c635efSGarrett D'Amore enum termfont f; 68395c635efSGarrett D'Amore 684*c66b8046SYuri Pankov if (p->flags & TERMP_NOBUF) { 685*c66b8046SYuri Pankov (*p->letter)(p, c); 686*c66b8046SYuri Pankov return; 687*c66b8046SYuri Pankov } 688*c66b8046SYuri Pankov 689*c66b8046SYuri Pankov if (p->col + 7 >= p->tcol->maxcols) 690*c66b8046SYuri Pankov adjbuf(p->tcol, p->col + 7); 691371584c2SYuri Pankov 692371584c2SYuri Pankov f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ? 693371584c2SYuri Pankov p->fontq[p->fonti] : TERMFONT_NONE; 694371584c2SYuri Pankov 695371584c2SYuri Pankov if (p->flags & TERMP_BACKBEFORE) { 696*c66b8046SYuri Pankov if (p->tcol->buf[p->col - 1] == ' ' || 697*c66b8046SYuri Pankov p->tcol->buf[p->col - 1] == '\t') 698371584c2SYuri Pankov p->col--; 699371584c2SYuri Pankov else 700*c66b8046SYuri Pankov p->tcol->buf[p->col++] = '\b'; 701371584c2SYuri Pankov p->flags &= ~TERMP_BACKBEFORE; 702698f87a4SGarrett D'Amore } 703*c66b8046SYuri Pankov if (f == TERMFONT_UNDER || f == TERMFONT_BI) { 704*c66b8046SYuri Pankov p->tcol->buf[p->col++] = '_'; 705*c66b8046SYuri Pankov p->tcol->buf[p->col++] = '\b'; 706698f87a4SGarrett D'Amore } 707*c66b8046SYuri Pankov if (f == TERMFONT_BOLD || f == TERMFONT_BI) { 708*c66b8046SYuri Pankov if (c == ASCII_HYPH) 709*c66b8046SYuri Pankov p->tcol->buf[p->col++] = '-'; 710698f87a4SGarrett D'Amore else 711*c66b8046SYuri Pankov p->tcol->buf[p->col++] = c; 712*c66b8046SYuri Pankov p->tcol->buf[p->col++] = '\b'; 713698f87a4SGarrett D'Amore } 714*c66b8046SYuri Pankov if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) 715*c66b8046SYuri Pankov p->tcol->buf[p->col] = c; 716*c66b8046SYuri Pankov if (p->tcol->lastcol < ++p->col) 717*c66b8046SYuri Pankov p->tcol->lastcol = p->col; 718371584c2SYuri Pankov if (p->flags & TERMP_BACKAFTER) { 719371584c2SYuri Pankov p->flags |= TERMP_BACKBEFORE; 720371584c2SYuri Pankov p->flags &= ~TERMP_BACKAFTER; 721371584c2SYuri Pankov } 72295c635efSGarrett D'Amore } 72395c635efSGarrett D'Amore 72495c635efSGarrett D'Amore static void 72595c635efSGarrett D'Amore encode(struct termp *p, const char *word, size_t sz) 72695c635efSGarrett D'Amore { 727698f87a4SGarrett D'Amore size_t i; 72895c635efSGarrett D'Amore 729*c66b8046SYuri Pankov if (p->flags & TERMP_NOBUF) { 730*c66b8046SYuri Pankov for (i = 0; i < sz; i++) 731*c66b8046SYuri Pankov (*p->letter)(p, word[i]); 732*c66b8046SYuri Pankov return; 733*c66b8046SYuri Pankov } 734*c66b8046SYuri Pankov 735*c66b8046SYuri Pankov if (p->col + 2 + (sz * 5) >= p->tcol->maxcols) 736*c66b8046SYuri Pankov adjbuf(p->tcol, p->col + 2 + (sz * 5)); 73795c635efSGarrett D'Amore 738698f87a4SGarrett D'Amore for (i = 0; i < sz; i++) { 739698f87a4SGarrett D'Amore if (ASCII_HYPH == word[i] || 740698f87a4SGarrett D'Amore isgraph((unsigned char)word[i])) 741698f87a4SGarrett D'Amore encode1(p, word[i]); 742a40ea1a7SYuri Pankov else { 743*c66b8046SYuri Pankov if (p->tcol->lastcol <= p->col || 744*c66b8046SYuri Pankov (word[i] != ' ' && word[i] != ASCII_NBRSP)) 745*c66b8046SYuri Pankov p->tcol->buf[p->col] = word[i]; 746*c66b8046SYuri Pankov p->col++; 747a40ea1a7SYuri Pankov 748a40ea1a7SYuri Pankov /* 749a40ea1a7SYuri Pankov * Postpone the effect of \z while handling 750a40ea1a7SYuri Pankov * an overstrike sequence from ascii_uc2str(). 751a40ea1a7SYuri Pankov */ 752a40ea1a7SYuri Pankov 753a40ea1a7SYuri Pankov if (word[i] == '\b' && 754a40ea1a7SYuri Pankov (p->flags & TERMP_BACKBEFORE)) { 755a40ea1a7SYuri Pankov p->flags &= ~TERMP_BACKBEFORE; 756a40ea1a7SYuri Pankov p->flags |= TERMP_BACKAFTER; 757a40ea1a7SYuri Pankov } 758a40ea1a7SYuri Pankov } 75995c635efSGarrett D'Amore } 760*c66b8046SYuri Pankov if (p->tcol->lastcol < p->col) 761*c66b8046SYuri Pankov p->tcol->lastcol = p->col; 76295c635efSGarrett D'Amore } 76395c635efSGarrett D'Amore 764260e9a87SYuri Pankov void 765260e9a87SYuri Pankov term_setwidth(struct termp *p, const char *wstr) 766260e9a87SYuri Pankov { 767260e9a87SYuri Pankov struct roffsu su; 768371584c2SYuri Pankov int iop, width; 769260e9a87SYuri Pankov 770260e9a87SYuri Pankov iop = 0; 771260e9a87SYuri Pankov width = 0; 772260e9a87SYuri Pankov if (NULL != wstr) { 773260e9a87SYuri Pankov switch (*wstr) { 774260e9a87SYuri Pankov case '+': 775260e9a87SYuri Pankov iop = 1; 776260e9a87SYuri Pankov wstr++; 777260e9a87SYuri Pankov break; 778260e9a87SYuri Pankov case '-': 779260e9a87SYuri Pankov iop = -1; 780260e9a87SYuri Pankov wstr++; 781260e9a87SYuri Pankov break; 782260e9a87SYuri Pankov default: 783260e9a87SYuri Pankov break; 784260e9a87SYuri Pankov } 785*c66b8046SYuri Pankov if (a2roffsu(wstr, &su, SCALE_MAX) != NULL) 786260e9a87SYuri Pankov width = term_hspan(p, &su); 787260e9a87SYuri Pankov else 788260e9a87SYuri Pankov iop = 0; 789260e9a87SYuri Pankov } 790260e9a87SYuri Pankov (*p->setwidth)(p, iop, width); 791260e9a87SYuri Pankov } 792260e9a87SYuri Pankov 79395c635efSGarrett D'Amore size_t 79495c635efSGarrett D'Amore term_len(const struct termp *p, size_t sz) 79595c635efSGarrett D'Amore { 79695c635efSGarrett D'Amore 797371584c2SYuri Pankov return (*p->width)(p, ' ') * sz; 79895c635efSGarrett D'Amore } 79995c635efSGarrett D'Amore 800698f87a4SGarrett D'Amore static size_t 801698f87a4SGarrett D'Amore cond_width(const struct termp *p, int c, int *skip) 802698f87a4SGarrett D'Amore { 803698f87a4SGarrett D'Amore 804698f87a4SGarrett D'Amore if (*skip) { 805698f87a4SGarrett D'Amore (*skip) = 0; 806371584c2SYuri Pankov return 0; 807698f87a4SGarrett D'Amore } else 808371584c2SYuri Pankov return (*p->width)(p, c); 809698f87a4SGarrett D'Amore } 81095c635efSGarrett D'Amore 81195c635efSGarrett D'Amore size_t 81295c635efSGarrett D'Amore term_strlen(const struct termp *p, const char *cp) 81395c635efSGarrett D'Amore { 81495c635efSGarrett D'Amore size_t sz, rsz, i; 815260e9a87SYuri Pankov int ssz, skip, uc; 81695c635efSGarrett D'Amore const char *seq, *rhs; 81795c635efSGarrett D'Amore enum mandoc_esc esc; 818260e9a87SYuri Pankov static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH, 819260e9a87SYuri Pankov ASCII_BREAK, '\0' }; 82095c635efSGarrett D'Amore 82195c635efSGarrett D'Amore /* 82295c635efSGarrett D'Amore * Account for escaped sequences within string length 82395c635efSGarrett D'Amore * calculations. This follows the logic in term_word() as we 82495c635efSGarrett D'Amore * must calculate the width of produced strings. 82595c635efSGarrett D'Amore */ 82695c635efSGarrett D'Amore 82795c635efSGarrett D'Amore sz = 0; 828698f87a4SGarrett D'Amore skip = 0; 82995c635efSGarrett D'Amore while ('\0' != *cp) { 83095c635efSGarrett D'Amore rsz = strcspn(cp, rej); 83195c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 832698f87a4SGarrett D'Amore sz += cond_width(p, *cp++, &skip); 83395c635efSGarrett D'Amore 83495c635efSGarrett D'Amore switch (*cp) { 835260e9a87SYuri Pankov case '\\': 83695c635efSGarrett D'Amore cp++; 83795c635efSGarrett D'Amore esc = mandoc_escape(&cp, &seq, &ssz); 83895c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 83995c635efSGarrett D'Amore continue; 84095c635efSGarrett D'Amore 84195c635efSGarrett D'Amore rhs = NULL; 84295c635efSGarrett D'Amore 84395c635efSGarrett D'Amore switch (esc) { 844260e9a87SYuri Pankov case ESCAPE_UNICODE: 845260e9a87SYuri Pankov uc = mchars_num2uc(seq + 1, ssz - 1); 84695c635efSGarrett D'Amore break; 847260e9a87SYuri Pankov case ESCAPE_NUMBERED: 848260e9a87SYuri Pankov uc = mchars_num2char(seq, ssz); 849260e9a87SYuri Pankov if (uc < 0) 850260e9a87SYuri Pankov continue; 85195c635efSGarrett D'Amore break; 852260e9a87SYuri Pankov case ESCAPE_SPECIAL: 853260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 854371584c2SYuri Pankov rhs = mchars_spec2str(seq, ssz, &rsz); 855260e9a87SYuri Pankov if (rhs != NULL) 85695c635efSGarrett D'Amore break; 857260e9a87SYuri Pankov } else { 858371584c2SYuri Pankov uc = mchars_spec2cp(seq, ssz); 859260e9a87SYuri Pankov if (uc > 0) 860260e9a87SYuri Pankov sz += cond_width(p, uc, &skip); 861260e9a87SYuri Pankov } 862260e9a87SYuri Pankov continue; 863260e9a87SYuri Pankov case ESCAPE_SKIPCHAR: 864698f87a4SGarrett D'Amore skip = 1; 865260e9a87SYuri Pankov continue; 866260e9a87SYuri Pankov case ESCAPE_OVERSTRIKE: 867260e9a87SYuri Pankov rsz = 0; 868260e9a87SYuri Pankov rhs = seq + ssz; 869260e9a87SYuri Pankov while (seq < rhs) { 870260e9a87SYuri Pankov if (*seq == '\\') { 871260e9a87SYuri Pankov mandoc_escape(&seq, NULL, NULL); 872260e9a87SYuri Pankov continue; 873260e9a87SYuri Pankov } 874260e9a87SYuri Pankov i = (*p->width)(p, *seq++); 875260e9a87SYuri Pankov if (rsz < i) 876260e9a87SYuri Pankov rsz = i; 877260e9a87SYuri Pankov } 878260e9a87SYuri Pankov sz += rsz; 879260e9a87SYuri Pankov continue; 88095c635efSGarrett D'Amore default: 881260e9a87SYuri Pankov continue; 88295c635efSGarrett D'Amore } 88395c635efSGarrett D'Amore 884260e9a87SYuri Pankov /* 885260e9a87SYuri Pankov * Common handling for Unicode and numbered 886260e9a87SYuri Pankov * character escape sequences. 887260e9a87SYuri Pankov */ 888260e9a87SYuri Pankov 889260e9a87SYuri Pankov if (rhs == NULL) { 890260e9a87SYuri Pankov if (p->enc == TERMENC_ASCII) { 891260e9a87SYuri Pankov rhs = ascii_uc2str(uc); 892260e9a87SYuri Pankov rsz = strlen(rhs); 893260e9a87SYuri Pankov } else { 894260e9a87SYuri Pankov if ((uc < 0x20 && uc != 0x09) || 895260e9a87SYuri Pankov (uc > 0x7E && uc < 0xA0)) 896260e9a87SYuri Pankov uc = 0xFFFD; 897260e9a87SYuri Pankov sz += cond_width(p, uc, &skip); 898260e9a87SYuri Pankov continue; 899260e9a87SYuri Pankov } 900260e9a87SYuri Pankov } 90195c635efSGarrett D'Amore 902698f87a4SGarrett D'Amore if (skip) { 903698f87a4SGarrett D'Amore skip = 0; 904698f87a4SGarrett D'Amore break; 905698f87a4SGarrett D'Amore } 906698f87a4SGarrett D'Amore 907260e9a87SYuri Pankov /* 908260e9a87SYuri Pankov * Common handling for all escape sequences 909260e9a87SYuri Pankov * printing more than one character. 910260e9a87SYuri Pankov */ 911260e9a87SYuri Pankov 91295c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 91395c635efSGarrett D'Amore sz += (*p->width)(p, *rhs++); 91495c635efSGarrett D'Amore break; 915260e9a87SYuri Pankov case ASCII_NBRSP: 916698f87a4SGarrett D'Amore sz += cond_width(p, ' ', &skip); 91795c635efSGarrett D'Amore cp++; 91895c635efSGarrett D'Amore break; 919260e9a87SYuri Pankov case ASCII_HYPH: 920698f87a4SGarrett D'Amore sz += cond_width(p, '-', &skip); 92195c635efSGarrett D'Amore cp++; 92295c635efSGarrett D'Amore break; 92395c635efSGarrett D'Amore default: 92495c635efSGarrett D'Amore break; 92595c635efSGarrett D'Amore } 92695c635efSGarrett D'Amore } 92795c635efSGarrett D'Amore 928371584c2SYuri Pankov return sz; 92995c635efSGarrett D'Amore } 93095c635efSGarrett D'Amore 931260e9a87SYuri Pankov int 93295c635efSGarrett D'Amore term_vspan(const struct termp *p, const struct roffsu *su) 93395c635efSGarrett D'Amore { 93495c635efSGarrett D'Amore double r; 935260e9a87SYuri Pankov int ri; 93695c635efSGarrett D'Amore 93795c635efSGarrett D'Amore switch (su->unit) { 938260e9a87SYuri Pankov case SCALE_BU: 939260e9a87SYuri Pankov r = su->scale / 40.0; 94095c635efSGarrett D'Amore break; 941260e9a87SYuri Pankov case SCALE_CM: 942260e9a87SYuri Pankov r = su->scale * 6.0 / 2.54; 94395c635efSGarrett D'Amore break; 944260e9a87SYuri Pankov case SCALE_FS: 945260e9a87SYuri Pankov r = su->scale * 65536.0 / 40.0; 946260e9a87SYuri Pankov break; 947260e9a87SYuri Pankov case SCALE_IN: 948260e9a87SYuri Pankov r = su->scale * 6.0; 949260e9a87SYuri Pankov break; 950260e9a87SYuri Pankov case SCALE_MM: 951260e9a87SYuri Pankov r = su->scale * 0.006; 952260e9a87SYuri Pankov break; 953260e9a87SYuri Pankov case SCALE_PC: 95495c635efSGarrett D'Amore r = su->scale; 95595c635efSGarrett D'Amore break; 956260e9a87SYuri Pankov case SCALE_PT: 957260e9a87SYuri Pankov r = su->scale / 12.0; 95895c635efSGarrett D'Amore break; 959260e9a87SYuri Pankov case SCALE_EN: 960260e9a87SYuri Pankov case SCALE_EM: 961260e9a87SYuri Pankov r = su->scale * 0.6; 96295c635efSGarrett D'Amore break; 963260e9a87SYuri Pankov case SCALE_VS: 96495c635efSGarrett D'Amore r = su->scale; 96595c635efSGarrett D'Amore break; 96695c635efSGarrett D'Amore default: 967260e9a87SYuri Pankov abort(); 968260e9a87SYuri Pankov } 969260e9a87SYuri Pankov ri = r > 0.0 ? r + 0.4995 : r - 0.4995; 970371584c2SYuri Pankov return ri < 66 ? ri : 1; 97195c635efSGarrett D'Amore } 97295c635efSGarrett D'Amore 973371584c2SYuri Pankov /* 974*c66b8046SYuri Pankov * Convert a scaling width to basic units, rounding towards 0. 975371584c2SYuri Pankov */ 976260e9a87SYuri Pankov int 97795c635efSGarrett D'Amore term_hspan(const struct termp *p, const struct roffsu *su) 97895c635efSGarrett D'Amore { 97995c635efSGarrett D'Amore 980371584c2SYuri Pankov return (*p->hspan)(p, su); 98195c635efSGarrett D'Amore } 982*c66b8046SYuri Pankov 983*c66b8046SYuri Pankov /* 984*c66b8046SYuri Pankov * Convert a scaling width to basic units, rounding to closest. 985*c66b8046SYuri Pankov */ 986*c66b8046SYuri Pankov int 987*c66b8046SYuri Pankov term_hen(const struct termp *p, const struct roffsu *su) 988*c66b8046SYuri Pankov { 989*c66b8046SYuri Pankov int bu; 990*c66b8046SYuri Pankov 991*c66b8046SYuri Pankov if ((bu = (*p->hspan)(p, su)) >= 0) 992*c66b8046SYuri Pankov return (bu + 11) / 24; 993*c66b8046SYuri Pankov else 994*c66b8046SYuri Pankov return -((-bu + 11) / 24); 995*c66b8046SYuri Pankov } 996