1*95c635efSGarrett D'Amore /* $Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */ 2*95c635efSGarrett D'Amore /* 3*95c635efSGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*95c635efSGarrett D'Amore * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5*95c635efSGarrett D'Amore * 6*95c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 7*95c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 8*95c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 9*95c635efSGarrett D'Amore * 10*95c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*95c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*95c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*95c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*95c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*95c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*95c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*95c635efSGarrett D'Amore */ 18*95c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H 19*95c635efSGarrett D'Amore #include "config.h" 20*95c635efSGarrett D'Amore #endif 21*95c635efSGarrett D'Amore 22*95c635efSGarrett D'Amore #include <sys/types.h> 23*95c635efSGarrett D'Amore 24*95c635efSGarrett D'Amore #include <assert.h> 25*95c635efSGarrett D'Amore #include <ctype.h> 26*95c635efSGarrett D'Amore #include <stdint.h> 27*95c635efSGarrett D'Amore #include <stdio.h> 28*95c635efSGarrett D'Amore #include <stdlib.h> 29*95c635efSGarrett D'Amore #include <string.h> 30*95c635efSGarrett D'Amore 31*95c635efSGarrett D'Amore #include "mandoc.h" 32*95c635efSGarrett D'Amore #include "out.h" 33*95c635efSGarrett D'Amore #include "term.h" 34*95c635efSGarrett D'Amore #include "main.h" 35*95c635efSGarrett D'Amore 36*95c635efSGarrett D'Amore static void adjbuf(struct termp *p, int); 37*95c635efSGarrett D'Amore static void bufferc(struct termp *, char); 38*95c635efSGarrett D'Amore static void encode(struct termp *, const char *, size_t); 39*95c635efSGarrett D'Amore static void encode1(struct termp *, int); 40*95c635efSGarrett D'Amore 41*95c635efSGarrett D'Amore void 42*95c635efSGarrett D'Amore term_free(struct termp *p) 43*95c635efSGarrett D'Amore { 44*95c635efSGarrett D'Amore 45*95c635efSGarrett D'Amore if (p->buf) 46*95c635efSGarrett D'Amore free(p->buf); 47*95c635efSGarrett D'Amore if (p->symtab) 48*95c635efSGarrett D'Amore mchars_free(p->symtab); 49*95c635efSGarrett D'Amore 50*95c635efSGarrett D'Amore free(p); 51*95c635efSGarrett D'Amore } 52*95c635efSGarrett D'Amore 53*95c635efSGarrett D'Amore 54*95c635efSGarrett D'Amore void 55*95c635efSGarrett D'Amore term_begin(struct termp *p, term_margin head, 56*95c635efSGarrett D'Amore term_margin foot, const void *arg) 57*95c635efSGarrett D'Amore { 58*95c635efSGarrett D'Amore 59*95c635efSGarrett D'Amore p->headf = head; 60*95c635efSGarrett D'Amore p->footf = foot; 61*95c635efSGarrett D'Amore p->argf = arg; 62*95c635efSGarrett D'Amore (*p->begin)(p); 63*95c635efSGarrett D'Amore } 64*95c635efSGarrett D'Amore 65*95c635efSGarrett D'Amore 66*95c635efSGarrett D'Amore void 67*95c635efSGarrett D'Amore term_end(struct termp *p) 68*95c635efSGarrett D'Amore { 69*95c635efSGarrett D'Amore 70*95c635efSGarrett D'Amore (*p->end)(p); 71*95c635efSGarrett D'Amore } 72*95c635efSGarrett D'Amore 73*95c635efSGarrett D'Amore /* 74*95c635efSGarrett D'Amore * Flush a line of text. A "line" is loosely defined as being something 75*95c635efSGarrett D'Amore * that should be followed by a newline, regardless of whether it's 76*95c635efSGarrett D'Amore * broken apart by newlines getting there. A line can also be a 77*95c635efSGarrett D'Amore * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does 78*95c635efSGarrett D'Amore * not have a trailing newline. 79*95c635efSGarrett D'Amore * 80*95c635efSGarrett D'Amore * The following flags may be specified: 81*95c635efSGarrett D'Amore * 82*95c635efSGarrett D'Amore * - TERMP_NOBREAK: this is the most important and is used when making 83*95c635efSGarrett D'Amore * columns. In short: don't print a newline and instead expect the 84*95c635efSGarrett D'Amore * next call to do the padding up to the start of the next column. 85*95c635efSGarrett D'Amore * 86*95c635efSGarrett D'Amore * - TERMP_TWOSPACE: make sure there is room for at least two space 87*95c635efSGarrett D'Amore * characters of padding. Otherwise, rather break the line. 88*95c635efSGarrett D'Amore * 89*95c635efSGarrett D'Amore * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and 90*95c635efSGarrett D'Amore * the line is overrun, and don't pad-right if it's underrun. 91*95c635efSGarrett D'Amore * 92*95c635efSGarrett D'Amore * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when 93*95c635efSGarrett D'Amore * overrunning, instead save the position and continue at that point 94*95c635efSGarrett D'Amore * when the next invocation. 95*95c635efSGarrett D'Amore * 96*95c635efSGarrett D'Amore * In-line line breaking: 97*95c635efSGarrett D'Amore * 98*95c635efSGarrett D'Amore * If TERMP_NOBREAK is specified and the line overruns the right 99*95c635efSGarrett D'Amore * margin, it will break and pad-right to the right margin after 100*95c635efSGarrett D'Amore * writing. If maxrmargin is violated, it will break and continue 101*95c635efSGarrett D'Amore * writing from the right-margin, which will lead to the above scenario 102*95c635efSGarrett D'Amore * upon exit. Otherwise, the line will break at the right margin. 103*95c635efSGarrett D'Amore */ 104*95c635efSGarrett D'Amore void 105*95c635efSGarrett D'Amore term_flushln(struct termp *p) 106*95c635efSGarrett D'Amore { 107*95c635efSGarrett D'Amore int i; /* current input position in p->buf */ 108*95c635efSGarrett D'Amore size_t vis; /* current visual position on output */ 109*95c635efSGarrett D'Amore size_t vbl; /* number of blanks to prepend to output */ 110*95c635efSGarrett D'Amore size_t vend; /* end of word visual position on output */ 111*95c635efSGarrett D'Amore size_t bp; /* visual right border position */ 112*95c635efSGarrett D'Amore size_t dv; /* temporary for visual pos calculations */ 113*95c635efSGarrett D'Amore int j; /* temporary loop index for p->buf */ 114*95c635efSGarrett D'Amore int jhy; /* last hyph before overflow w/r/t j */ 115*95c635efSGarrett D'Amore size_t maxvis; /* output position of visible boundary */ 116*95c635efSGarrett D'Amore size_t mmax; /* used in calculating bp */ 117*95c635efSGarrett D'Amore 118*95c635efSGarrett D'Amore /* 119*95c635efSGarrett D'Amore * First, establish the maximum columns of "visible" content. 120*95c635efSGarrett D'Amore * This is usually the difference between the right-margin and 121*95c635efSGarrett D'Amore * an indentation, but can be, for tagged lists or columns, a 122*95c635efSGarrett D'Amore * small set of values. 123*95c635efSGarrett D'Amore */ 124*95c635efSGarrett D'Amore assert (p->rmargin >= p->offset); 125*95c635efSGarrett D'Amore dv = p->rmargin - p->offset; 126*95c635efSGarrett D'Amore maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 127*95c635efSGarrett D'Amore dv = p->maxrmargin - p->offset; 128*95c635efSGarrett D'Amore mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; 129*95c635efSGarrett D'Amore 130*95c635efSGarrett D'Amore bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; 131*95c635efSGarrett D'Amore 132*95c635efSGarrett D'Amore /* 133*95c635efSGarrett D'Amore * Calculate the required amount of padding. 134*95c635efSGarrett D'Amore */ 135*95c635efSGarrett D'Amore vbl = p->offset + p->overstep > p->viscol ? 136*95c635efSGarrett D'Amore p->offset + p->overstep - p->viscol : 0; 137*95c635efSGarrett D'Amore 138*95c635efSGarrett D'Amore vis = vend = 0; 139*95c635efSGarrett D'Amore i = 0; 140*95c635efSGarrett D'Amore 141*95c635efSGarrett D'Amore while (i < p->col) { 142*95c635efSGarrett D'Amore /* 143*95c635efSGarrett D'Amore * Handle literal tab characters: collapse all 144*95c635efSGarrett D'Amore * subsequent tabs into a single huge set of spaces. 145*95c635efSGarrett D'Amore */ 146*95c635efSGarrett D'Amore while (i < p->col && '\t' == p->buf[i]) { 147*95c635efSGarrett D'Amore vend = (vis / p->tabwidth + 1) * p->tabwidth; 148*95c635efSGarrett D'Amore vbl += vend - vis; 149*95c635efSGarrett D'Amore vis = vend; 150*95c635efSGarrett D'Amore i++; 151*95c635efSGarrett D'Amore } 152*95c635efSGarrett D'Amore 153*95c635efSGarrett D'Amore /* 154*95c635efSGarrett D'Amore * Count up visible word characters. Control sequences 155*95c635efSGarrett D'Amore * (starting with the CSI) aren't counted. A space 156*95c635efSGarrett D'Amore * generates a non-printing word, which is valid (the 157*95c635efSGarrett D'Amore * space is printed according to regular spacing rules). 158*95c635efSGarrett D'Amore */ 159*95c635efSGarrett D'Amore 160*95c635efSGarrett D'Amore for (j = i, jhy = 0; j < p->col; j++) { 161*95c635efSGarrett D'Amore if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j]) 162*95c635efSGarrett D'Amore break; 163*95c635efSGarrett D'Amore 164*95c635efSGarrett D'Amore /* Back over the the last printed character. */ 165*95c635efSGarrett D'Amore if (8 == p->buf[j]) { 166*95c635efSGarrett D'Amore assert(j); 167*95c635efSGarrett D'Amore vend -= (*p->width)(p, p->buf[j - 1]); 168*95c635efSGarrett D'Amore continue; 169*95c635efSGarrett D'Amore } 170*95c635efSGarrett D'Amore 171*95c635efSGarrett D'Amore /* Regular word. */ 172*95c635efSGarrett D'Amore /* Break at the hyphen point if we overrun. */ 173*95c635efSGarrett D'Amore if (vend > vis && vend < bp && 174*95c635efSGarrett D'Amore ASCII_HYPH == p->buf[j]) 175*95c635efSGarrett D'Amore jhy = j; 176*95c635efSGarrett D'Amore 177*95c635efSGarrett D'Amore vend += (*p->width)(p, p->buf[j]); 178*95c635efSGarrett D'Amore } 179*95c635efSGarrett D'Amore 180*95c635efSGarrett D'Amore /* 181*95c635efSGarrett D'Amore * Find out whether we would exceed the right margin. 182*95c635efSGarrett D'Amore * If so, break to the next line. 183*95c635efSGarrett D'Amore */ 184*95c635efSGarrett D'Amore if (vend > bp && 0 == jhy && vis > 0) { 185*95c635efSGarrett D'Amore vend -= vis; 186*95c635efSGarrett D'Amore (*p->endline)(p); 187*95c635efSGarrett D'Amore p->viscol = 0; 188*95c635efSGarrett D'Amore if (TERMP_NOBREAK & p->flags) { 189*95c635efSGarrett D'Amore vbl = p->rmargin; 190*95c635efSGarrett D'Amore vend += p->rmargin - p->offset; 191*95c635efSGarrett D'Amore } else 192*95c635efSGarrett D'Amore vbl = p->offset; 193*95c635efSGarrett D'Amore 194*95c635efSGarrett D'Amore /* Remove the p->overstep width. */ 195*95c635efSGarrett D'Amore 196*95c635efSGarrett D'Amore bp += (size_t)p->overstep; 197*95c635efSGarrett D'Amore p->overstep = 0; 198*95c635efSGarrett D'Amore } 199*95c635efSGarrett D'Amore 200*95c635efSGarrett D'Amore /* Write out the [remaining] word. */ 201*95c635efSGarrett D'Amore for ( ; i < p->col; i++) { 202*95c635efSGarrett D'Amore if (vend > bp && jhy > 0 && i > jhy) 203*95c635efSGarrett D'Amore break; 204*95c635efSGarrett D'Amore if ('\t' == p->buf[i]) 205*95c635efSGarrett D'Amore break; 206*95c635efSGarrett D'Amore if (' ' == p->buf[i]) { 207*95c635efSGarrett D'Amore j = i; 208*95c635efSGarrett D'Amore while (' ' == p->buf[i]) 209*95c635efSGarrett D'Amore i++; 210*95c635efSGarrett D'Amore dv = (size_t)(i - j) * (*p->width)(p, ' '); 211*95c635efSGarrett D'Amore vbl += dv; 212*95c635efSGarrett D'Amore vend += dv; 213*95c635efSGarrett D'Amore break; 214*95c635efSGarrett D'Amore } 215*95c635efSGarrett D'Amore if (ASCII_NBRSP == p->buf[i]) { 216*95c635efSGarrett D'Amore vbl += (*p->width)(p, ' '); 217*95c635efSGarrett D'Amore continue; 218*95c635efSGarrett D'Amore } 219*95c635efSGarrett D'Amore 220*95c635efSGarrett D'Amore /* 221*95c635efSGarrett D'Amore * Now we definitely know there will be 222*95c635efSGarrett D'Amore * printable characters to output, 223*95c635efSGarrett D'Amore * so write preceding white space now. 224*95c635efSGarrett D'Amore */ 225*95c635efSGarrett D'Amore if (vbl) { 226*95c635efSGarrett D'Amore (*p->advance)(p, vbl); 227*95c635efSGarrett D'Amore p->viscol += vbl; 228*95c635efSGarrett D'Amore vbl = 0; 229*95c635efSGarrett D'Amore } 230*95c635efSGarrett D'Amore 231*95c635efSGarrett D'Amore if (ASCII_HYPH == p->buf[i]) { 232*95c635efSGarrett D'Amore (*p->letter)(p, '-'); 233*95c635efSGarrett D'Amore p->viscol += (*p->width)(p, '-'); 234*95c635efSGarrett D'Amore continue; 235*95c635efSGarrett D'Amore } 236*95c635efSGarrett D'Amore 237*95c635efSGarrett D'Amore (*p->letter)(p, p->buf[i]); 238*95c635efSGarrett D'Amore if (8 == p->buf[i]) 239*95c635efSGarrett D'Amore p->viscol -= (*p->width)(p, p->buf[i-1]); 240*95c635efSGarrett D'Amore else 241*95c635efSGarrett D'Amore p->viscol += (*p->width)(p, p->buf[i]); 242*95c635efSGarrett D'Amore } 243*95c635efSGarrett D'Amore vis = vend; 244*95c635efSGarrett D'Amore } 245*95c635efSGarrett D'Amore 246*95c635efSGarrett D'Amore /* 247*95c635efSGarrett D'Amore * If there was trailing white space, it was not printed; 248*95c635efSGarrett D'Amore * so reset the cursor position accordingly. 249*95c635efSGarrett D'Amore */ 250*95c635efSGarrett D'Amore if (vis) 251*95c635efSGarrett D'Amore vis -= vbl; 252*95c635efSGarrett D'Amore 253*95c635efSGarrett D'Amore p->col = 0; 254*95c635efSGarrett D'Amore p->overstep = 0; 255*95c635efSGarrett D'Amore 256*95c635efSGarrett D'Amore if ( ! (TERMP_NOBREAK & p->flags)) { 257*95c635efSGarrett D'Amore p->viscol = 0; 258*95c635efSGarrett D'Amore (*p->endline)(p); 259*95c635efSGarrett D'Amore return; 260*95c635efSGarrett D'Amore } 261*95c635efSGarrett D'Amore 262*95c635efSGarrett D'Amore if (TERMP_HANG & p->flags) { 263*95c635efSGarrett D'Amore /* We need one blank after the tag. */ 264*95c635efSGarrett D'Amore p->overstep = (int)(vis - maxvis + (*p->width)(p, ' ')); 265*95c635efSGarrett D'Amore 266*95c635efSGarrett D'Amore /* 267*95c635efSGarrett D'Amore * Behave exactly the same way as groff: 268*95c635efSGarrett D'Amore * If we have overstepped the margin, temporarily move 269*95c635efSGarrett D'Amore * it to the right and flag the rest of the line to be 270*95c635efSGarrett D'Amore * shorter. 271*95c635efSGarrett D'Amore * If we landed right at the margin, be happy. 272*95c635efSGarrett D'Amore * If we are one step before the margin, temporarily 273*95c635efSGarrett D'Amore * move it one step LEFT and flag the rest of the line 274*95c635efSGarrett D'Amore * to be longer. 275*95c635efSGarrett D'Amore */ 276*95c635efSGarrett D'Amore if (p->overstep < -1) 277*95c635efSGarrett D'Amore p->overstep = 0; 278*95c635efSGarrett D'Amore return; 279*95c635efSGarrett D'Amore 280*95c635efSGarrett D'Amore } else if (TERMP_DANGLE & p->flags) 281*95c635efSGarrett D'Amore return; 282*95c635efSGarrett D'Amore 283*95c635efSGarrett D'Amore /* If the column was overrun, break the line. */ 284*95c635efSGarrett D'Amore if (maxvis <= vis + 285*95c635efSGarrett D'Amore ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) { 286*95c635efSGarrett D'Amore (*p->endline)(p); 287*95c635efSGarrett D'Amore p->viscol = 0; 288*95c635efSGarrett D'Amore } 289*95c635efSGarrett D'Amore } 290*95c635efSGarrett D'Amore 291*95c635efSGarrett D'Amore 292*95c635efSGarrett D'Amore /* 293*95c635efSGarrett D'Amore * A newline only breaks an existing line; it won't assert vertical 294*95c635efSGarrett D'Amore * space. All data in the output buffer is flushed prior to the newline 295*95c635efSGarrett D'Amore * assertion. 296*95c635efSGarrett D'Amore */ 297*95c635efSGarrett D'Amore void 298*95c635efSGarrett D'Amore term_newln(struct termp *p) 299*95c635efSGarrett D'Amore { 300*95c635efSGarrett D'Amore 301*95c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 302*95c635efSGarrett D'Amore if (p->col || p->viscol) 303*95c635efSGarrett D'Amore term_flushln(p); 304*95c635efSGarrett D'Amore } 305*95c635efSGarrett D'Amore 306*95c635efSGarrett D'Amore 307*95c635efSGarrett D'Amore /* 308*95c635efSGarrett D'Amore * Asserts a vertical space (a full, empty line-break between lines). 309*95c635efSGarrett D'Amore * Note that if used twice, this will cause two blank spaces and so on. 310*95c635efSGarrett D'Amore * All data in the output buffer is flushed prior to the newline 311*95c635efSGarrett D'Amore * assertion. 312*95c635efSGarrett D'Amore */ 313*95c635efSGarrett D'Amore void 314*95c635efSGarrett D'Amore term_vspace(struct termp *p) 315*95c635efSGarrett D'Amore { 316*95c635efSGarrett D'Amore 317*95c635efSGarrett D'Amore term_newln(p); 318*95c635efSGarrett D'Amore p->viscol = 0; 319*95c635efSGarrett D'Amore (*p->endline)(p); 320*95c635efSGarrett D'Amore } 321*95c635efSGarrett D'Amore 322*95c635efSGarrett D'Amore void 323*95c635efSGarrett D'Amore term_fontlast(struct termp *p) 324*95c635efSGarrett D'Amore { 325*95c635efSGarrett D'Amore enum termfont f; 326*95c635efSGarrett D'Amore 327*95c635efSGarrett D'Amore f = p->fontl; 328*95c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 329*95c635efSGarrett D'Amore p->fontq[p->fonti] = f; 330*95c635efSGarrett D'Amore } 331*95c635efSGarrett D'Amore 332*95c635efSGarrett D'Amore 333*95c635efSGarrett D'Amore void 334*95c635efSGarrett D'Amore term_fontrepl(struct termp *p, enum termfont f) 335*95c635efSGarrett D'Amore { 336*95c635efSGarrett D'Amore 337*95c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 338*95c635efSGarrett D'Amore p->fontq[p->fonti] = f; 339*95c635efSGarrett D'Amore } 340*95c635efSGarrett D'Amore 341*95c635efSGarrett D'Amore 342*95c635efSGarrett D'Amore void 343*95c635efSGarrett D'Amore term_fontpush(struct termp *p, enum termfont f) 344*95c635efSGarrett D'Amore { 345*95c635efSGarrett D'Amore 346*95c635efSGarrett D'Amore assert(p->fonti + 1 < 10); 347*95c635efSGarrett D'Amore p->fontl = p->fontq[p->fonti]; 348*95c635efSGarrett D'Amore p->fontq[++p->fonti] = f; 349*95c635efSGarrett D'Amore } 350*95c635efSGarrett D'Amore 351*95c635efSGarrett D'Amore 352*95c635efSGarrett D'Amore const void * 353*95c635efSGarrett D'Amore term_fontq(struct termp *p) 354*95c635efSGarrett D'Amore { 355*95c635efSGarrett D'Amore 356*95c635efSGarrett D'Amore return(&p->fontq[p->fonti]); 357*95c635efSGarrett D'Amore } 358*95c635efSGarrett D'Amore 359*95c635efSGarrett D'Amore 360*95c635efSGarrett D'Amore enum termfont 361*95c635efSGarrett D'Amore term_fonttop(struct termp *p) 362*95c635efSGarrett D'Amore { 363*95c635efSGarrett D'Amore 364*95c635efSGarrett D'Amore return(p->fontq[p->fonti]); 365*95c635efSGarrett D'Amore } 366*95c635efSGarrett D'Amore 367*95c635efSGarrett D'Amore 368*95c635efSGarrett D'Amore void 369*95c635efSGarrett D'Amore term_fontpopq(struct termp *p, const void *key) 370*95c635efSGarrett D'Amore { 371*95c635efSGarrett D'Amore 372*95c635efSGarrett D'Amore while (p->fonti >= 0 && key != &p->fontq[p->fonti]) 373*95c635efSGarrett D'Amore p->fonti--; 374*95c635efSGarrett D'Amore assert(p->fonti >= 0); 375*95c635efSGarrett D'Amore } 376*95c635efSGarrett D'Amore 377*95c635efSGarrett D'Amore 378*95c635efSGarrett D'Amore void 379*95c635efSGarrett D'Amore term_fontpop(struct termp *p) 380*95c635efSGarrett D'Amore { 381*95c635efSGarrett D'Amore 382*95c635efSGarrett D'Amore assert(p->fonti); 383*95c635efSGarrett D'Amore p->fonti--; 384*95c635efSGarrett D'Amore } 385*95c635efSGarrett D'Amore 386*95c635efSGarrett D'Amore /* 387*95c635efSGarrett D'Amore * Handle pwords, partial words, which may be either a single word or a 388*95c635efSGarrett D'Amore * phrase that cannot be broken down (such as a literal string). This 389*95c635efSGarrett D'Amore * handles word styling. 390*95c635efSGarrett D'Amore */ 391*95c635efSGarrett D'Amore void 392*95c635efSGarrett D'Amore term_word(struct termp *p, const char *word) 393*95c635efSGarrett D'Amore { 394*95c635efSGarrett D'Amore const char *seq, *cp; 395*95c635efSGarrett D'Amore char c; 396*95c635efSGarrett D'Amore int sz, uc; 397*95c635efSGarrett D'Amore size_t ssz; 398*95c635efSGarrett D'Amore enum mandoc_esc esc; 399*95c635efSGarrett D'Amore 400*95c635efSGarrett D'Amore if ( ! (TERMP_NOSPACE & p->flags)) { 401*95c635efSGarrett D'Amore if ( ! (TERMP_KEEP & p->flags)) { 402*95c635efSGarrett D'Amore if (TERMP_PREKEEP & p->flags) 403*95c635efSGarrett D'Amore p->flags |= TERMP_KEEP; 404*95c635efSGarrett D'Amore bufferc(p, ' '); 405*95c635efSGarrett D'Amore if (TERMP_SENTENCE & p->flags) 406*95c635efSGarrett D'Amore bufferc(p, ' '); 407*95c635efSGarrett D'Amore } else 408*95c635efSGarrett D'Amore bufferc(p, ASCII_NBRSP); 409*95c635efSGarrett D'Amore } 410*95c635efSGarrett D'Amore 411*95c635efSGarrett D'Amore if ( ! (p->flags & TERMP_NONOSPACE)) 412*95c635efSGarrett D'Amore p->flags &= ~TERMP_NOSPACE; 413*95c635efSGarrett D'Amore else 414*95c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 415*95c635efSGarrett D'Amore 416*95c635efSGarrett D'Amore p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM); 417*95c635efSGarrett D'Amore 418*95c635efSGarrett D'Amore while ('\0' != *word) { 419*95c635efSGarrett D'Amore if ((ssz = strcspn(word, "\\")) > 0) 420*95c635efSGarrett D'Amore encode(p, word, ssz); 421*95c635efSGarrett D'Amore 422*95c635efSGarrett D'Amore word += (int)ssz; 423*95c635efSGarrett D'Amore if ('\\' != *word) 424*95c635efSGarrett D'Amore continue; 425*95c635efSGarrett D'Amore 426*95c635efSGarrett D'Amore word++; 427*95c635efSGarrett D'Amore esc = mandoc_escape(&word, &seq, &sz); 428*95c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 429*95c635efSGarrett D'Amore break; 430*95c635efSGarrett D'Amore 431*95c635efSGarrett D'Amore if (TERMENC_ASCII != p->enc) 432*95c635efSGarrett D'Amore switch (esc) { 433*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 434*95c635efSGarrett D'Amore uc = mchars_num2uc(seq + 1, sz - 1); 435*95c635efSGarrett D'Amore if ('\0' == uc) 436*95c635efSGarrett D'Amore break; 437*95c635efSGarrett D'Amore encode1(p, uc); 438*95c635efSGarrett D'Amore continue; 439*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 440*95c635efSGarrett D'Amore uc = mchars_spec2cp(p->symtab, seq, sz); 441*95c635efSGarrett D'Amore if (uc <= 0) 442*95c635efSGarrett D'Amore break; 443*95c635efSGarrett D'Amore encode1(p, uc); 444*95c635efSGarrett D'Amore continue; 445*95c635efSGarrett D'Amore default: 446*95c635efSGarrett D'Amore break; 447*95c635efSGarrett D'Amore } 448*95c635efSGarrett D'Amore 449*95c635efSGarrett D'Amore switch (esc) { 450*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 451*95c635efSGarrett D'Amore encode1(p, '?'); 452*95c635efSGarrett D'Amore break; 453*95c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 454*95c635efSGarrett D'Amore c = mchars_num2char(seq, sz); 455*95c635efSGarrett D'Amore if ('\0' != c) 456*95c635efSGarrett D'Amore encode(p, &c, 1); 457*95c635efSGarrett D'Amore break; 458*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 459*95c635efSGarrett D'Amore cp = mchars_spec2str(p->symtab, seq, sz, &ssz); 460*95c635efSGarrett D'Amore if (NULL != cp) 461*95c635efSGarrett D'Amore encode(p, cp, ssz); 462*95c635efSGarrett D'Amore else if (1 == ssz) 463*95c635efSGarrett D'Amore encode(p, seq, sz); 464*95c635efSGarrett D'Amore break; 465*95c635efSGarrett D'Amore case (ESCAPE_FONTBOLD): 466*95c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_BOLD); 467*95c635efSGarrett D'Amore break; 468*95c635efSGarrett D'Amore case (ESCAPE_FONTITALIC): 469*95c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_UNDER); 470*95c635efSGarrett D'Amore break; 471*95c635efSGarrett D'Amore case (ESCAPE_FONT): 472*95c635efSGarrett D'Amore /* FALLTHROUGH */ 473*95c635efSGarrett D'Amore case (ESCAPE_FONTROMAN): 474*95c635efSGarrett D'Amore term_fontrepl(p, TERMFONT_NONE); 475*95c635efSGarrett D'Amore break; 476*95c635efSGarrett D'Amore case (ESCAPE_FONTPREV): 477*95c635efSGarrett D'Amore term_fontlast(p); 478*95c635efSGarrett D'Amore break; 479*95c635efSGarrett D'Amore case (ESCAPE_NOSPACE): 480*95c635efSGarrett D'Amore if ('\0' == *word) 481*95c635efSGarrett D'Amore p->flags |= TERMP_NOSPACE; 482*95c635efSGarrett D'Amore break; 483*95c635efSGarrett D'Amore default: 484*95c635efSGarrett D'Amore break; 485*95c635efSGarrett D'Amore } 486*95c635efSGarrett D'Amore } 487*95c635efSGarrett D'Amore } 488*95c635efSGarrett D'Amore 489*95c635efSGarrett D'Amore static void 490*95c635efSGarrett D'Amore adjbuf(struct termp *p, int sz) 491*95c635efSGarrett D'Amore { 492*95c635efSGarrett D'Amore 493*95c635efSGarrett D'Amore if (0 == p->maxcols) 494*95c635efSGarrett D'Amore p->maxcols = 1024; 495*95c635efSGarrett D'Amore while (sz >= p->maxcols) 496*95c635efSGarrett D'Amore p->maxcols <<= 2; 497*95c635efSGarrett D'Amore 498*95c635efSGarrett D'Amore p->buf = mandoc_realloc 499*95c635efSGarrett D'Amore (p->buf, sizeof(int) * (size_t)p->maxcols); 500*95c635efSGarrett D'Amore } 501*95c635efSGarrett D'Amore 502*95c635efSGarrett D'Amore static void 503*95c635efSGarrett D'Amore bufferc(struct termp *p, char c) 504*95c635efSGarrett D'Amore { 505*95c635efSGarrett D'Amore 506*95c635efSGarrett D'Amore if (p->col + 1 >= p->maxcols) 507*95c635efSGarrett D'Amore adjbuf(p, p->col + 1); 508*95c635efSGarrett D'Amore 509*95c635efSGarrett D'Amore p->buf[p->col++] = c; 510*95c635efSGarrett D'Amore } 511*95c635efSGarrett D'Amore 512*95c635efSGarrett D'Amore /* 513*95c635efSGarrett D'Amore * See encode(). 514*95c635efSGarrett D'Amore * Do this for a single (probably unicode) value. 515*95c635efSGarrett D'Amore * Does not check for non-decorated glyphs. 516*95c635efSGarrett D'Amore */ 517*95c635efSGarrett D'Amore static void 518*95c635efSGarrett D'Amore encode1(struct termp *p, int c) 519*95c635efSGarrett D'Amore { 520*95c635efSGarrett D'Amore enum termfont f; 521*95c635efSGarrett D'Amore 522*95c635efSGarrett D'Amore if (p->col + 4 >= p->maxcols) 523*95c635efSGarrett D'Amore adjbuf(p, p->col + 4); 524*95c635efSGarrett D'Amore 525*95c635efSGarrett D'Amore f = term_fonttop(p); 526*95c635efSGarrett D'Amore 527*95c635efSGarrett D'Amore if (TERMFONT_NONE == f) { 528*95c635efSGarrett D'Amore p->buf[p->col++] = c; 529*95c635efSGarrett D'Amore return; 530*95c635efSGarrett D'Amore } else if (TERMFONT_UNDER == f) { 531*95c635efSGarrett D'Amore p->buf[p->col++] = '_'; 532*95c635efSGarrett D'Amore } else 533*95c635efSGarrett D'Amore p->buf[p->col++] = c; 534*95c635efSGarrett D'Amore 535*95c635efSGarrett D'Amore p->buf[p->col++] = 8; 536*95c635efSGarrett D'Amore p->buf[p->col++] = c; 537*95c635efSGarrett D'Amore } 538*95c635efSGarrett D'Amore 539*95c635efSGarrett D'Amore static void 540*95c635efSGarrett D'Amore encode(struct termp *p, const char *word, size_t sz) 541*95c635efSGarrett D'Amore { 542*95c635efSGarrett D'Amore enum termfont f; 543*95c635efSGarrett D'Amore int i, len; 544*95c635efSGarrett D'Amore 545*95c635efSGarrett D'Amore /* LINTED */ 546*95c635efSGarrett D'Amore len = sz; 547*95c635efSGarrett D'Amore 548*95c635efSGarrett D'Amore /* 549*95c635efSGarrett D'Amore * Encode and buffer a string of characters. If the current 550*95c635efSGarrett D'Amore * font mode is unset, buffer directly, else encode then buffer 551*95c635efSGarrett D'Amore * character by character. 552*95c635efSGarrett D'Amore */ 553*95c635efSGarrett D'Amore 554*95c635efSGarrett D'Amore if (TERMFONT_NONE == (f = term_fonttop(p))) { 555*95c635efSGarrett D'Amore if (p->col + len >= p->maxcols) 556*95c635efSGarrett D'Amore adjbuf(p, p->col + len); 557*95c635efSGarrett D'Amore for (i = 0; i < len; i++) 558*95c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 559*95c635efSGarrett D'Amore return; 560*95c635efSGarrett D'Amore } 561*95c635efSGarrett D'Amore 562*95c635efSGarrett D'Amore /* Pre-buffer, assuming worst-case. */ 563*95c635efSGarrett D'Amore 564*95c635efSGarrett D'Amore if (p->col + 1 + (len * 3) >= p->maxcols) 565*95c635efSGarrett D'Amore adjbuf(p, p->col + 1 + (len * 3)); 566*95c635efSGarrett D'Amore 567*95c635efSGarrett D'Amore for (i = 0; i < len; i++) { 568*95c635efSGarrett D'Amore if (ASCII_HYPH != word[i] && 569*95c635efSGarrett D'Amore ! isgraph((unsigned char)word[i])) { 570*95c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 571*95c635efSGarrett D'Amore continue; 572*95c635efSGarrett D'Amore } 573*95c635efSGarrett D'Amore 574*95c635efSGarrett D'Amore if (TERMFONT_UNDER == f) 575*95c635efSGarrett D'Amore p->buf[p->col++] = '_'; 576*95c635efSGarrett D'Amore else if (ASCII_HYPH == word[i]) 577*95c635efSGarrett D'Amore p->buf[p->col++] = '-'; 578*95c635efSGarrett D'Amore else 579*95c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 580*95c635efSGarrett D'Amore 581*95c635efSGarrett D'Amore p->buf[p->col++] = 8; 582*95c635efSGarrett D'Amore p->buf[p->col++] = word[i]; 583*95c635efSGarrett D'Amore } 584*95c635efSGarrett D'Amore } 585*95c635efSGarrett D'Amore 586*95c635efSGarrett D'Amore size_t 587*95c635efSGarrett D'Amore term_len(const struct termp *p, size_t sz) 588*95c635efSGarrett D'Amore { 589*95c635efSGarrett D'Amore 590*95c635efSGarrett D'Amore return((*p->width)(p, ' ') * sz); 591*95c635efSGarrett D'Amore } 592*95c635efSGarrett D'Amore 593*95c635efSGarrett D'Amore 594*95c635efSGarrett D'Amore size_t 595*95c635efSGarrett D'Amore term_strlen(const struct termp *p, const char *cp) 596*95c635efSGarrett D'Amore { 597*95c635efSGarrett D'Amore size_t sz, rsz, i; 598*95c635efSGarrett D'Amore int ssz, c; 599*95c635efSGarrett D'Amore const char *seq, *rhs; 600*95c635efSGarrett D'Amore enum mandoc_esc esc; 601*95c635efSGarrett D'Amore static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' }; 602*95c635efSGarrett D'Amore 603*95c635efSGarrett D'Amore /* 604*95c635efSGarrett D'Amore * Account for escaped sequences within string length 605*95c635efSGarrett D'Amore * calculations. This follows the logic in term_word() as we 606*95c635efSGarrett D'Amore * must calculate the width of produced strings. 607*95c635efSGarrett D'Amore */ 608*95c635efSGarrett D'Amore 609*95c635efSGarrett D'Amore sz = 0; 610*95c635efSGarrett D'Amore while ('\0' != *cp) { 611*95c635efSGarrett D'Amore rsz = strcspn(cp, rej); 612*95c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 613*95c635efSGarrett D'Amore sz += (*p->width)(p, *cp++); 614*95c635efSGarrett D'Amore 615*95c635efSGarrett D'Amore c = 0; 616*95c635efSGarrett D'Amore switch (*cp) { 617*95c635efSGarrett D'Amore case ('\\'): 618*95c635efSGarrett D'Amore cp++; 619*95c635efSGarrett D'Amore esc = mandoc_escape(&cp, &seq, &ssz); 620*95c635efSGarrett D'Amore if (ESCAPE_ERROR == esc) 621*95c635efSGarrett D'Amore return(sz); 622*95c635efSGarrett D'Amore 623*95c635efSGarrett D'Amore if (TERMENC_ASCII != p->enc) 624*95c635efSGarrett D'Amore switch (esc) { 625*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 626*95c635efSGarrett D'Amore c = mchars_num2uc 627*95c635efSGarrett D'Amore (seq + 1, ssz - 1); 628*95c635efSGarrett D'Amore if ('\0' == c) 629*95c635efSGarrett D'Amore break; 630*95c635efSGarrett D'Amore sz += (*p->width)(p, c); 631*95c635efSGarrett D'Amore continue; 632*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 633*95c635efSGarrett D'Amore c = mchars_spec2cp 634*95c635efSGarrett D'Amore (p->symtab, seq, ssz); 635*95c635efSGarrett D'Amore if (c <= 0) 636*95c635efSGarrett D'Amore break; 637*95c635efSGarrett D'Amore sz += (*p->width)(p, c); 638*95c635efSGarrett D'Amore continue; 639*95c635efSGarrett D'Amore default: 640*95c635efSGarrett D'Amore break; 641*95c635efSGarrett D'Amore } 642*95c635efSGarrett D'Amore 643*95c635efSGarrett D'Amore rhs = NULL; 644*95c635efSGarrett D'Amore 645*95c635efSGarrett D'Amore switch (esc) { 646*95c635efSGarrett D'Amore case (ESCAPE_UNICODE): 647*95c635efSGarrett D'Amore sz += (*p->width)(p, '?'); 648*95c635efSGarrett D'Amore break; 649*95c635efSGarrett D'Amore case (ESCAPE_NUMBERED): 650*95c635efSGarrett D'Amore c = mchars_num2char(seq, ssz); 651*95c635efSGarrett D'Amore if ('\0' != c) 652*95c635efSGarrett D'Amore sz += (*p->width)(p, c); 653*95c635efSGarrett D'Amore break; 654*95c635efSGarrett D'Amore case (ESCAPE_SPECIAL): 655*95c635efSGarrett D'Amore rhs = mchars_spec2str 656*95c635efSGarrett D'Amore (p->symtab, seq, ssz, &rsz); 657*95c635efSGarrett D'Amore 658*95c635efSGarrett D'Amore if (ssz != 1 || rhs) 659*95c635efSGarrett D'Amore break; 660*95c635efSGarrett D'Amore 661*95c635efSGarrett D'Amore rhs = seq; 662*95c635efSGarrett D'Amore rsz = ssz; 663*95c635efSGarrett D'Amore break; 664*95c635efSGarrett D'Amore default: 665*95c635efSGarrett D'Amore break; 666*95c635efSGarrett D'Amore } 667*95c635efSGarrett D'Amore 668*95c635efSGarrett D'Amore if (NULL == rhs) 669*95c635efSGarrett D'Amore break; 670*95c635efSGarrett D'Amore 671*95c635efSGarrett D'Amore for (i = 0; i < rsz; i++) 672*95c635efSGarrett D'Amore sz += (*p->width)(p, *rhs++); 673*95c635efSGarrett D'Amore break; 674*95c635efSGarrett D'Amore case (ASCII_NBRSP): 675*95c635efSGarrett D'Amore sz += (*p->width)(p, ' '); 676*95c635efSGarrett D'Amore cp++; 677*95c635efSGarrett D'Amore break; 678*95c635efSGarrett D'Amore case (ASCII_HYPH): 679*95c635efSGarrett D'Amore sz += (*p->width)(p, '-'); 680*95c635efSGarrett D'Amore cp++; 681*95c635efSGarrett D'Amore break; 682*95c635efSGarrett D'Amore default: 683*95c635efSGarrett D'Amore break; 684*95c635efSGarrett D'Amore } 685*95c635efSGarrett D'Amore } 686*95c635efSGarrett D'Amore 687*95c635efSGarrett D'Amore return(sz); 688*95c635efSGarrett D'Amore } 689*95c635efSGarrett D'Amore 690*95c635efSGarrett D'Amore /* ARGSUSED */ 691*95c635efSGarrett D'Amore size_t 692*95c635efSGarrett D'Amore term_vspan(const struct termp *p, const struct roffsu *su) 693*95c635efSGarrett D'Amore { 694*95c635efSGarrett D'Amore double r; 695*95c635efSGarrett D'Amore 696*95c635efSGarrett D'Amore switch (su->unit) { 697*95c635efSGarrett D'Amore case (SCALE_CM): 698*95c635efSGarrett D'Amore r = su->scale * 2; 699*95c635efSGarrett D'Amore break; 700*95c635efSGarrett D'Amore case (SCALE_IN): 701*95c635efSGarrett D'Amore r = su->scale * 6; 702*95c635efSGarrett D'Amore break; 703*95c635efSGarrett D'Amore case (SCALE_PC): 704*95c635efSGarrett D'Amore r = su->scale; 705*95c635efSGarrett D'Amore break; 706*95c635efSGarrett D'Amore case (SCALE_PT): 707*95c635efSGarrett D'Amore r = su->scale / 8; 708*95c635efSGarrett D'Amore break; 709*95c635efSGarrett D'Amore case (SCALE_MM): 710*95c635efSGarrett D'Amore r = su->scale / 1000; 711*95c635efSGarrett D'Amore break; 712*95c635efSGarrett D'Amore case (SCALE_VS): 713*95c635efSGarrett D'Amore r = su->scale; 714*95c635efSGarrett D'Amore break; 715*95c635efSGarrett D'Amore default: 716*95c635efSGarrett D'Amore r = su->scale - 1; 717*95c635efSGarrett D'Amore break; 718*95c635efSGarrett D'Amore } 719*95c635efSGarrett D'Amore 720*95c635efSGarrett D'Amore if (r < 0.0) 721*95c635efSGarrett D'Amore r = 0.0; 722*95c635efSGarrett D'Amore return(/* LINTED */(size_t) 723*95c635efSGarrett D'Amore r); 724*95c635efSGarrett D'Amore } 725*95c635efSGarrett D'Amore 726*95c635efSGarrett D'Amore size_t 727*95c635efSGarrett D'Amore term_hspan(const struct termp *p, const struct roffsu *su) 728*95c635efSGarrett D'Amore { 729*95c635efSGarrett D'Amore double v; 730*95c635efSGarrett D'Amore 731*95c635efSGarrett D'Amore v = ((*p->hspan)(p, su)); 732*95c635efSGarrett D'Amore if (v < 0.0) 733*95c635efSGarrett D'Amore v = 0.0; 734*95c635efSGarrett D'Amore return((size_t) /* LINTED */ 735*95c635efSGarrett D'Amore v); 736*95c635efSGarrett D'Amore } 737