1*c1c95addSBrooks Davis /* $Id: term.c,v 1.291 2023/04/28 19:11:04 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 3*c1c95addSBrooks Davis * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org> 461d06d6bSBaptiste Daroussin * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 2161d06d6bSBaptiste Daroussin 2261d06d6bSBaptiste Daroussin #include <assert.h> 2361d06d6bSBaptiste Daroussin #include <ctype.h> 247295610fSBaptiste Daroussin #include <stdint.h> 2561d06d6bSBaptiste Daroussin #include <stdio.h> 2661d06d6bSBaptiste Daroussin #include <stdlib.h> 2761d06d6bSBaptiste Daroussin #include <string.h> 2861d06d6bSBaptiste Daroussin 2961d06d6bSBaptiste Daroussin #include "mandoc.h" 3061d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3161d06d6bSBaptiste Daroussin #include "out.h" 3261d06d6bSBaptiste Daroussin #include "term.h" 3361d06d6bSBaptiste Daroussin #include "main.h" 3461d06d6bSBaptiste Daroussin 3561d06d6bSBaptiste Daroussin static size_t cond_width(const struct termp *, int, int *); 3661d06d6bSBaptiste Daroussin static void adjbuf(struct termp_col *, size_t); 3761d06d6bSBaptiste Daroussin static void bufferc(struct termp *, char); 3861d06d6bSBaptiste Daroussin static void encode(struct termp *, const char *, size_t); 3961d06d6bSBaptiste Daroussin static void encode1(struct termp *, int); 4061d06d6bSBaptiste Daroussin static void endline(struct termp *); 416d38604fSBaptiste Daroussin static void term_field(struct termp *, size_t, size_t); 427295610fSBaptiste Daroussin static void term_fill(struct termp *, size_t *, size_t *, 437295610fSBaptiste Daroussin size_t); 4461d06d6bSBaptiste Daroussin 4561d06d6bSBaptiste Daroussin 4661d06d6bSBaptiste Daroussin void 4761d06d6bSBaptiste Daroussin term_setcol(struct termp *p, size_t maxtcol) 4861d06d6bSBaptiste Daroussin { 4961d06d6bSBaptiste Daroussin if (maxtcol > p->maxtcol) { 5061d06d6bSBaptiste Daroussin p->tcols = mandoc_recallocarray(p->tcols, 5161d06d6bSBaptiste Daroussin p->maxtcol, maxtcol, sizeof(*p->tcols)); 5261d06d6bSBaptiste Daroussin p->maxtcol = maxtcol; 5361d06d6bSBaptiste Daroussin } 5461d06d6bSBaptiste Daroussin p->lasttcol = maxtcol - 1; 5561d06d6bSBaptiste Daroussin p->tcol = p->tcols; 5661d06d6bSBaptiste Daroussin } 5761d06d6bSBaptiste Daroussin 5861d06d6bSBaptiste Daroussin void 5961d06d6bSBaptiste Daroussin term_free(struct termp *p) 6061d06d6bSBaptiste Daroussin { 61*c1c95addSBrooks Davis term_tab_free(); 6261d06d6bSBaptiste Daroussin for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++) 6361d06d6bSBaptiste Daroussin free(p->tcol->buf); 6461d06d6bSBaptiste Daroussin free(p->tcols); 6561d06d6bSBaptiste Daroussin free(p->fontq); 6661d06d6bSBaptiste Daroussin free(p); 6761d06d6bSBaptiste Daroussin } 6861d06d6bSBaptiste Daroussin 6961d06d6bSBaptiste Daroussin void 7061d06d6bSBaptiste Daroussin term_begin(struct termp *p, term_margin head, 7161d06d6bSBaptiste Daroussin term_margin foot, const struct roff_meta *arg) 7261d06d6bSBaptiste Daroussin { 7361d06d6bSBaptiste Daroussin 7461d06d6bSBaptiste Daroussin p->headf = head; 7561d06d6bSBaptiste Daroussin p->footf = foot; 7661d06d6bSBaptiste Daroussin p->argf = arg; 7761d06d6bSBaptiste Daroussin (*p->begin)(p); 7861d06d6bSBaptiste Daroussin } 7961d06d6bSBaptiste Daroussin 8061d06d6bSBaptiste Daroussin void 8161d06d6bSBaptiste Daroussin term_end(struct termp *p) 8261d06d6bSBaptiste Daroussin { 8361d06d6bSBaptiste Daroussin 8461d06d6bSBaptiste Daroussin (*p->end)(p); 8561d06d6bSBaptiste Daroussin } 8661d06d6bSBaptiste Daroussin 8761d06d6bSBaptiste Daroussin /* 8861d06d6bSBaptiste Daroussin * Flush a chunk of text. By default, break the output line each time 8961d06d6bSBaptiste Daroussin * the right margin is reached, and continue output on the next line 9061d06d6bSBaptiste Daroussin * at the same offset as the chunk itself. By default, also break the 917295610fSBaptiste Daroussin * output line at the end of the chunk. There are many flags modifying 927295610fSBaptiste Daroussin * this behaviour, see the comments in the body of the function. 9361d06d6bSBaptiste Daroussin */ 9461d06d6bSBaptiste Daroussin void 9561d06d6bSBaptiste Daroussin term_flushln(struct termp *p) 9661d06d6bSBaptiste Daroussin { 977295610fSBaptiste Daroussin size_t vbl; /* Number of blanks to prepend to the output. */ 987295610fSBaptiste Daroussin size_t vbr; /* Actual visual position of the end of field. */ 997295610fSBaptiste Daroussin size_t vfield; /* Desired visual field width. */ 1007295610fSBaptiste Daroussin size_t vtarget; /* Desired visual position of the right margin. */ 1017295610fSBaptiste Daroussin size_t ic; /* Character position in the input buffer. */ 1027295610fSBaptiste Daroussin size_t nbr; /* Number of characters to print in this field. */ 1037295610fSBaptiste Daroussin 1047295610fSBaptiste Daroussin /* 1057295610fSBaptiste Daroussin * Normally, start writing at the left margin, but with the 1067295610fSBaptiste Daroussin * NOPAD flag, start writing at the current position instead. 1077295610fSBaptiste Daroussin */ 10861d06d6bSBaptiste Daroussin 10961d06d6bSBaptiste Daroussin vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ? 11061d06d6bSBaptiste Daroussin 0 : p->tcol->offset - p->viscol; 11161d06d6bSBaptiste Daroussin if (p->minbl && vbl < p->minbl) 11261d06d6bSBaptiste Daroussin vbl = p->minbl; 11361d06d6bSBaptiste Daroussin 11461d06d6bSBaptiste Daroussin if ((p->flags & TERMP_MULTICOL) == 0) 11561d06d6bSBaptiste Daroussin p->tcol->col = 0; 1167295610fSBaptiste Daroussin 1177295610fSBaptiste Daroussin /* Loop over output lines. */ 1187295610fSBaptiste Daroussin 1197295610fSBaptiste Daroussin for (;;) { 1207295610fSBaptiste Daroussin vfield = p->tcol->rmargin > p->viscol + vbl ? 1217295610fSBaptiste Daroussin p->tcol->rmargin - p->viscol - vbl : 0; 12261d06d6bSBaptiste Daroussin 12361d06d6bSBaptiste Daroussin /* 1247295610fSBaptiste Daroussin * Normally, break the line at the the right margin 1257295610fSBaptiste Daroussin * of the field, but with the NOBREAK flag, only 1267295610fSBaptiste Daroussin * break it at the max right margin of the screen, 1277295610fSBaptiste Daroussin * and with the BRNEVER flag, never break it at all. 12861d06d6bSBaptiste Daroussin */ 12961d06d6bSBaptiste Daroussin 1306d38604fSBaptiste Daroussin vtarget = (p->flags & TERMP_NOBREAK) == 0 ? vfield : 1317295610fSBaptiste Daroussin p->maxrmargin > p->viscol + vbl ? 1327295610fSBaptiste Daroussin p->maxrmargin - p->viscol - vbl : 0; 13361d06d6bSBaptiste Daroussin 13461d06d6bSBaptiste Daroussin /* 1357295610fSBaptiste Daroussin * Figure out how much text will fit in the field. 1367295610fSBaptiste Daroussin * If there is whitespace only, print nothing. 13761d06d6bSBaptiste Daroussin */ 13861d06d6bSBaptiste Daroussin 1396d38604fSBaptiste Daroussin term_fill(p, &nbr, &vbr, 1406d38604fSBaptiste Daroussin p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget); 1417295610fSBaptiste Daroussin if (nbr == 0) 14261d06d6bSBaptiste Daroussin break; 14361d06d6bSBaptiste Daroussin 1447295610fSBaptiste Daroussin /* 1457295610fSBaptiste Daroussin * With the CENTER or RIGHT flag, increase the indentation 1467295610fSBaptiste Daroussin * to center the text between the left and right margins 1477295610fSBaptiste Daroussin * or to adjust it to the right margin, respectively. 1487295610fSBaptiste Daroussin */ 1497295610fSBaptiste Daroussin 1507295610fSBaptiste Daroussin if (vbr < vtarget) { 1517295610fSBaptiste Daroussin if (p->flags & TERMP_CENTER) 1527295610fSBaptiste Daroussin vbl += (vtarget - vbr) / 2; 1537295610fSBaptiste Daroussin else if (p->flags & TERMP_RIGHT) 1547295610fSBaptiste Daroussin vbl += vtarget - vbr; 1557295610fSBaptiste Daroussin } 1567295610fSBaptiste Daroussin 1577295610fSBaptiste Daroussin /* Finally, print the field content. */ 1587295610fSBaptiste Daroussin 1596d38604fSBaptiste Daroussin term_field(p, vbl, nbr); 160*c1c95addSBrooks Davis if (vbr < vtarget) 161*c1c95addSBrooks Davis p->tcol->taboff += vbr; 162*c1c95addSBrooks Davis else 163*c1c95addSBrooks Davis p->tcol->taboff += vtarget; 164*c1c95addSBrooks Davis p->tcol->taboff += (*p->width)(p, ' '); 1657295610fSBaptiste Daroussin 1667295610fSBaptiste Daroussin /* 1677295610fSBaptiste Daroussin * If there is no text left in the field, exit the loop. 1687295610fSBaptiste Daroussin * If the BRTRSP flag is set, consider trailing 1697295610fSBaptiste Daroussin * whitespace significant when deciding whether 1707295610fSBaptiste Daroussin * the field fits or not. 1717295610fSBaptiste Daroussin */ 1727295610fSBaptiste Daroussin 1737295610fSBaptiste Daroussin for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) { 1747295610fSBaptiste Daroussin switch (p->tcol->buf[ic]) { 1757295610fSBaptiste Daroussin case '\t': 1767295610fSBaptiste Daroussin if (p->flags & TERMP_BRTRSP) 1777295610fSBaptiste Daroussin vbr = term_tab_next(vbr); 17861d06d6bSBaptiste Daroussin continue; 1797295610fSBaptiste Daroussin case ' ': 1807295610fSBaptiste Daroussin if (p->flags & TERMP_BRTRSP) 1817295610fSBaptiste Daroussin vbr += (*p->width)(p, ' '); 18261d06d6bSBaptiste Daroussin continue; 1837295610fSBaptiste Daroussin case '\n': 184*c1c95addSBrooks Davis case ASCII_NBRZW: 1857295610fSBaptiste Daroussin case ASCII_BREAK: 186*c1c95addSBrooks Davis case ASCII_TABREF: 1877295610fSBaptiste Daroussin continue; 1887295610fSBaptiste Daroussin default: 18961d06d6bSBaptiste Daroussin break; 1907295610fSBaptiste Daroussin } 1917295610fSBaptiste Daroussin break; 1927295610fSBaptiste Daroussin } 1937295610fSBaptiste Daroussin if (ic == p->tcol->lastcol) 1947295610fSBaptiste Daroussin break; 1957295610fSBaptiste Daroussin 1967295610fSBaptiste Daroussin /* 197*c1c95addSBrooks Davis * At the location of an automatic line break, input 1987295610fSBaptiste Daroussin * space characters are consumed by the line break. 1997295610fSBaptiste Daroussin */ 2007295610fSBaptiste Daroussin 20161d06d6bSBaptiste Daroussin while (p->tcol->col < p->tcol->lastcol && 20261d06d6bSBaptiste Daroussin p->tcol->buf[p->tcol->col] == ' ') 20361d06d6bSBaptiste Daroussin p->tcol->col++; 20461d06d6bSBaptiste Daroussin 20561d06d6bSBaptiste Daroussin /* 2067295610fSBaptiste Daroussin * In multi-column mode, leave the rest of the text 2077295610fSBaptiste Daroussin * in the buffer to be handled by a subsequent 2087295610fSBaptiste Daroussin * invocation, such that the other columns of the 2097295610fSBaptiste Daroussin * table can be handled first. 2107295610fSBaptiste Daroussin * In single-column mode, simply break the line. 21161d06d6bSBaptiste Daroussin */ 21261d06d6bSBaptiste Daroussin 21361d06d6bSBaptiste Daroussin if (p->flags & TERMP_MULTICOL) 21461d06d6bSBaptiste Daroussin return; 21561d06d6bSBaptiste Daroussin 21661d06d6bSBaptiste Daroussin endline(p); 21761d06d6bSBaptiste Daroussin 21861d06d6bSBaptiste Daroussin /* 2197295610fSBaptiste Daroussin * Normally, start the next line at the same indentation 2207295610fSBaptiste Daroussin * as this one, but with the BRIND flag, start it at the 2217295610fSBaptiste Daroussin * right margin instead. This is used together with 2227295610fSBaptiste Daroussin * NOBREAK for the tags in various kinds of tagged lists. 22361d06d6bSBaptiste Daroussin */ 22461d06d6bSBaptiste Daroussin 2257295610fSBaptiste Daroussin vbl = p->flags & TERMP_BRIND ? 2267295610fSBaptiste Daroussin p->tcol->rmargin : p->tcol->offset; 2277295610fSBaptiste Daroussin } 2287295610fSBaptiste Daroussin 2297295610fSBaptiste Daroussin /* Reset output state in preparation for the next field. */ 23061d06d6bSBaptiste Daroussin 23161d06d6bSBaptiste Daroussin p->col = p->tcol->col = p->tcol->lastcol = 0; 23261d06d6bSBaptiste Daroussin p->minbl = p->trailspace; 23361d06d6bSBaptiste Daroussin p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); 23461d06d6bSBaptiste Daroussin 23561d06d6bSBaptiste Daroussin if (p->flags & TERMP_MULTICOL) 23661d06d6bSBaptiste Daroussin return; 23761d06d6bSBaptiste Daroussin 2387295610fSBaptiste Daroussin /* 2397295610fSBaptiste Daroussin * The HANG flag means that the next field 2407295610fSBaptiste Daroussin * always follows on the same line. 2417295610fSBaptiste Daroussin * The NOBREAK flag means that the next field 2427295610fSBaptiste Daroussin * follows on the same line unless the field was overrun. 2437295610fSBaptiste Daroussin * Normally, break the line at the end of each field. 2447295610fSBaptiste Daroussin */ 24561d06d6bSBaptiste Daroussin 2467295610fSBaptiste Daroussin if ((p->flags & TERMP_HANG) == 0 && 2477295610fSBaptiste Daroussin ((p->flags & TERMP_NOBREAK) == 0 || 2487295610fSBaptiste Daroussin vbr + term_len(p, p->trailspace) > vfield)) 24961d06d6bSBaptiste Daroussin endline(p); 25061d06d6bSBaptiste Daroussin } 25161d06d6bSBaptiste Daroussin 2527295610fSBaptiste Daroussin /* 2537295610fSBaptiste Daroussin * Store the number of input characters to print in this field in *nbr 2547295610fSBaptiste Daroussin * and their total visual width to print in *vbr. 2557295610fSBaptiste Daroussin * If there is only whitespace in the field, both remain zero. 2567295610fSBaptiste Daroussin * The desired visual width of the field is provided by vtarget. 2577295610fSBaptiste Daroussin * If the first word is longer, the field will be overrun. 2587295610fSBaptiste Daroussin */ 2597295610fSBaptiste Daroussin static void 2607295610fSBaptiste Daroussin term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget) 2617295610fSBaptiste Daroussin { 2627295610fSBaptiste Daroussin size_t ic; /* Character position in the input buffer. */ 2637295610fSBaptiste Daroussin size_t vis; /* Visual position of the current character. */ 2647295610fSBaptiste Daroussin size_t vn; /* Visual position of the next character. */ 2657295610fSBaptiste Daroussin int breakline; /* Break at the end of this word. */ 2667295610fSBaptiste Daroussin int graph; /* Last character was non-blank. */ 267*c1c95addSBrooks Davis int taboff; /* Temporary offset for literal tabs. */ 2687295610fSBaptiste Daroussin 2697295610fSBaptiste Daroussin *nbr = *vbr = vis = 0; 2707295610fSBaptiste Daroussin breakline = graph = 0; 271*c1c95addSBrooks Davis taboff = p->tcol->taboff; 2727295610fSBaptiste Daroussin for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) { 2737295610fSBaptiste Daroussin switch (p->tcol->buf[ic]) { 2747295610fSBaptiste Daroussin case '\b': /* Escape \o (overstrike) or backspace markup. */ 2757295610fSBaptiste Daroussin assert(ic > 0); 2767295610fSBaptiste Daroussin vis -= (*p->width)(p, p->tcol->buf[ic - 1]); 2777295610fSBaptiste Daroussin continue; 2787295610fSBaptiste Daroussin 2797295610fSBaptiste Daroussin case ' ': 2807295610fSBaptiste Daroussin case ASCII_BREAK: /* Escape \: (breakpoint). */ 2817295610fSBaptiste Daroussin vn = vis; 282*c1c95addSBrooks Davis if (p->tcol->buf[ic] == ' ') 283*c1c95addSBrooks Davis vn += (*p->width)(p, ' '); 2847295610fSBaptiste Daroussin /* Can break at the end of a word. */ 2857295610fSBaptiste Daroussin if (breakline || vn > vtarget) 2867295610fSBaptiste Daroussin break; 2877295610fSBaptiste Daroussin if (graph) { 2887295610fSBaptiste Daroussin *nbr = ic; 2897295610fSBaptiste Daroussin *vbr = vis; 2907295610fSBaptiste Daroussin graph = 0; 2917295610fSBaptiste Daroussin } 2927295610fSBaptiste Daroussin vis = vn; 2937295610fSBaptiste Daroussin continue; 2947295610fSBaptiste Daroussin 2957295610fSBaptiste Daroussin case '\n': /* Escape \p (break at the end of the word). */ 2967295610fSBaptiste Daroussin breakline = 1; 2977295610fSBaptiste Daroussin continue; 2987295610fSBaptiste Daroussin 2997295610fSBaptiste Daroussin case ASCII_HYPH: /* Breakable hyphen. */ 3007295610fSBaptiste Daroussin graph = 1; 3017295610fSBaptiste Daroussin /* 3027295610fSBaptiste Daroussin * We are about to decide whether to break the 3037295610fSBaptiste Daroussin * line or not, so we no longer need this hyphen 3047295610fSBaptiste Daroussin * to be marked as breakable. Put back a real 3057295610fSBaptiste Daroussin * hyphen such that we get the correct width. 3067295610fSBaptiste Daroussin */ 3077295610fSBaptiste Daroussin p->tcol->buf[ic] = '-'; 3087295610fSBaptiste Daroussin vis += (*p->width)(p, '-'); 3097295610fSBaptiste Daroussin if (vis > vtarget) { 3107295610fSBaptiste Daroussin ic++; 3117295610fSBaptiste Daroussin break; 3127295610fSBaptiste Daroussin } 3137295610fSBaptiste Daroussin *nbr = ic + 1; 3147295610fSBaptiste Daroussin *vbr = vis; 3157295610fSBaptiste Daroussin continue; 3167295610fSBaptiste Daroussin 317*c1c95addSBrooks Davis case ASCII_TABREF: 318*c1c95addSBrooks Davis taboff = -vis - (*p->width)(p, ' '); 319*c1c95addSBrooks Davis continue; 320*c1c95addSBrooks Davis 321*c1c95addSBrooks Davis default: 322*c1c95addSBrooks Davis switch (p->tcol->buf[ic]) { 323*c1c95addSBrooks Davis case '\t': 324*c1c95addSBrooks Davis if (taboff < 0 && (size_t)-taboff > vis) 325*c1c95addSBrooks Davis vis = 0; 326*c1c95addSBrooks Davis else 327*c1c95addSBrooks Davis vis += taboff; 328*c1c95addSBrooks Davis vis = term_tab_next(vis); 329*c1c95addSBrooks Davis vis -= taboff; 330*c1c95addSBrooks Davis break; 331*c1c95addSBrooks Davis case ASCII_NBRZW: /* Non-breakable zero-width. */ 332*c1c95addSBrooks Davis break; 3337295610fSBaptiste Daroussin case ASCII_NBRSP: /* Non-breakable space. */ 3347295610fSBaptiste Daroussin p->tcol->buf[ic] = ' '; 3357295610fSBaptiste Daroussin /* FALLTHROUGH */ 3367295610fSBaptiste Daroussin default: /* Printable character. */ 3377295610fSBaptiste Daroussin vis += (*p->width)(p, p->tcol->buf[ic]); 338*c1c95addSBrooks Davis break; 339*c1c95addSBrooks Davis } 340*c1c95addSBrooks Davis graph = 1; 3417295610fSBaptiste Daroussin if (vis > vtarget && *nbr > 0) 3427295610fSBaptiste Daroussin return; 3437295610fSBaptiste Daroussin continue; 3447295610fSBaptiste Daroussin } 3457295610fSBaptiste Daroussin break; 3467295610fSBaptiste Daroussin } 3477295610fSBaptiste Daroussin 3487295610fSBaptiste Daroussin /* 3497295610fSBaptiste Daroussin * If the last word extends to the end of the field without any 3507295610fSBaptiste Daroussin * trailing whitespace, the loop could not check yet whether it 3517295610fSBaptiste Daroussin * can remain on this line. So do the check now. 3527295610fSBaptiste Daroussin */ 3537295610fSBaptiste Daroussin 3547295610fSBaptiste Daroussin if (graph && (vis <= vtarget || *nbr == 0)) { 3557295610fSBaptiste Daroussin *nbr = ic; 3567295610fSBaptiste Daroussin *vbr = vis; 3577295610fSBaptiste Daroussin } 3587295610fSBaptiste Daroussin } 3597295610fSBaptiste Daroussin 3607295610fSBaptiste Daroussin /* 3617295610fSBaptiste Daroussin * Print the contents of one field 3627295610fSBaptiste Daroussin * with an indentation of vbl visual columns, 3636d38604fSBaptiste Daroussin * and an input string length of nbr characters. 3647295610fSBaptiste Daroussin */ 3657295610fSBaptiste Daroussin static void 3666d38604fSBaptiste Daroussin term_field(struct termp *p, size_t vbl, size_t nbr) 3677295610fSBaptiste Daroussin { 3687295610fSBaptiste Daroussin size_t ic; /* Character position in the input buffer. */ 3697295610fSBaptiste Daroussin size_t vis; /* Visual position of the current character. */ 370*c1c95addSBrooks Davis size_t vt; /* Visual position including tab offset. */ 3717295610fSBaptiste Daroussin size_t dv; /* Visual width of the current character. */ 372*c1c95addSBrooks Davis int taboff; /* Temporary offset for literal tabs. */ 3737295610fSBaptiste Daroussin 3747295610fSBaptiste Daroussin vis = 0; 375*c1c95addSBrooks Davis taboff = p->tcol->taboff; 3767295610fSBaptiste Daroussin for (ic = p->tcol->col; ic < nbr; ic++) { 3777295610fSBaptiste Daroussin 3787295610fSBaptiste Daroussin /* 3797295610fSBaptiste Daroussin * To avoid the printing of trailing whitespace, 3807295610fSBaptiste Daroussin * do not print whitespace right away, only count it. 3817295610fSBaptiste Daroussin */ 3827295610fSBaptiste Daroussin 3837295610fSBaptiste Daroussin switch (p->tcol->buf[ic]) { 3847295610fSBaptiste Daroussin case '\n': 3857295610fSBaptiste Daroussin case ASCII_BREAK: 386*c1c95addSBrooks Davis case ASCII_NBRZW: 387*c1c95addSBrooks Davis continue; 388*c1c95addSBrooks Davis case ASCII_TABREF: 389*c1c95addSBrooks Davis taboff = -vis - (*p->width)(p, ' '); 3907295610fSBaptiste Daroussin continue; 3917295610fSBaptiste Daroussin case '\t': 3927295610fSBaptiste Daroussin case ' ': 3937295610fSBaptiste Daroussin case ASCII_NBRSP: 394*c1c95addSBrooks Davis if (p->tcol->buf[ic] == '\t') { 395*c1c95addSBrooks Davis if (taboff < 0 && (size_t)-taboff > vis) 396*c1c95addSBrooks Davis vt = 0; 397*c1c95addSBrooks Davis else 398*c1c95addSBrooks Davis vt = vis + taboff; 399*c1c95addSBrooks Davis dv = term_tab_next(vt) - vt; 400*c1c95addSBrooks Davis } else 4017295610fSBaptiste Daroussin dv = (*p->width)(p, ' '); 4027295610fSBaptiste Daroussin vbl += dv; 4037295610fSBaptiste Daroussin vis += dv; 4047295610fSBaptiste Daroussin continue; 4057295610fSBaptiste Daroussin default: 4067295610fSBaptiste Daroussin break; 4077295610fSBaptiste Daroussin } 4087295610fSBaptiste Daroussin 4097295610fSBaptiste Daroussin /* 4107295610fSBaptiste Daroussin * We found a non-blank character to print, 4117295610fSBaptiste Daroussin * so write preceding white space now. 4127295610fSBaptiste Daroussin */ 4137295610fSBaptiste Daroussin 4147295610fSBaptiste Daroussin if (vbl > 0) { 4157295610fSBaptiste Daroussin (*p->advance)(p, vbl); 4167295610fSBaptiste Daroussin p->viscol += vbl; 4177295610fSBaptiste Daroussin vbl = 0; 4187295610fSBaptiste Daroussin } 4197295610fSBaptiste Daroussin 4207295610fSBaptiste Daroussin /* Print the character and adjust the visual position. */ 4217295610fSBaptiste Daroussin 4227295610fSBaptiste Daroussin (*p->letter)(p, p->tcol->buf[ic]); 4237295610fSBaptiste Daroussin if (p->tcol->buf[ic] == '\b') { 4247295610fSBaptiste Daroussin dv = (*p->width)(p, p->tcol->buf[ic - 1]); 4257295610fSBaptiste Daroussin p->viscol -= dv; 4267295610fSBaptiste Daroussin vis -= dv; 4277295610fSBaptiste Daroussin } else { 4287295610fSBaptiste Daroussin dv = (*p->width)(p, p->tcol->buf[ic]); 4297295610fSBaptiste Daroussin p->viscol += dv; 4307295610fSBaptiste Daroussin vis += dv; 4317295610fSBaptiste Daroussin } 4327295610fSBaptiste Daroussin } 4337295610fSBaptiste Daroussin p->tcol->col = nbr; 4347295610fSBaptiste Daroussin } 4357295610fSBaptiste Daroussin 43661d06d6bSBaptiste Daroussin static void 43761d06d6bSBaptiste Daroussin endline(struct termp *p) 43861d06d6bSBaptiste Daroussin { 43961d06d6bSBaptiste Daroussin if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) { 44061d06d6bSBaptiste Daroussin p->mc = NULL; 44161d06d6bSBaptiste Daroussin p->flags &= ~TERMP_ENDMC; 44261d06d6bSBaptiste Daroussin } 44361d06d6bSBaptiste Daroussin if (p->mc != NULL) { 44461d06d6bSBaptiste Daroussin if (p->viscol && p->maxrmargin >= p->viscol) 44561d06d6bSBaptiste Daroussin (*p->advance)(p, p->maxrmargin - p->viscol + 1); 44661d06d6bSBaptiste Daroussin p->flags |= TERMP_NOBUF | TERMP_NOSPACE; 44761d06d6bSBaptiste Daroussin term_word(p, p->mc); 44861d06d6bSBaptiste Daroussin p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC); 44961d06d6bSBaptiste Daroussin } 45061d06d6bSBaptiste Daroussin p->viscol = 0; 45161d06d6bSBaptiste Daroussin p->minbl = 0; 45261d06d6bSBaptiste Daroussin (*p->endline)(p); 45361d06d6bSBaptiste Daroussin } 45461d06d6bSBaptiste Daroussin 45561d06d6bSBaptiste Daroussin /* 45661d06d6bSBaptiste Daroussin * A newline only breaks an existing line; it won't assert vertical 45761d06d6bSBaptiste Daroussin * space. All data in the output buffer is flushed prior to the newline 45861d06d6bSBaptiste Daroussin * assertion. 45961d06d6bSBaptiste Daroussin */ 46061d06d6bSBaptiste Daroussin void 46161d06d6bSBaptiste Daroussin term_newln(struct termp *p) 46261d06d6bSBaptiste Daroussin { 46361d06d6bSBaptiste Daroussin p->flags |= TERMP_NOSPACE; 46461d06d6bSBaptiste Daroussin if (p->tcol->lastcol || p->viscol) 46561d06d6bSBaptiste Daroussin term_flushln(p); 466*c1c95addSBrooks Davis p->tcol->taboff = 0; 46761d06d6bSBaptiste Daroussin } 46861d06d6bSBaptiste Daroussin 46961d06d6bSBaptiste Daroussin /* 47061d06d6bSBaptiste Daroussin * Asserts a vertical space (a full, empty line-break between lines). 47161d06d6bSBaptiste Daroussin * Note that if used twice, this will cause two blank spaces and so on. 47261d06d6bSBaptiste Daroussin * All data in the output buffer is flushed prior to the newline 47361d06d6bSBaptiste Daroussin * assertion. 47461d06d6bSBaptiste Daroussin */ 47561d06d6bSBaptiste Daroussin void 47661d06d6bSBaptiste Daroussin term_vspace(struct termp *p) 47761d06d6bSBaptiste Daroussin { 47861d06d6bSBaptiste Daroussin 47961d06d6bSBaptiste Daroussin term_newln(p); 48061d06d6bSBaptiste Daroussin p->viscol = 0; 48161d06d6bSBaptiste Daroussin p->minbl = 0; 48261d06d6bSBaptiste Daroussin if (0 < p->skipvsp) 48361d06d6bSBaptiste Daroussin p->skipvsp--; 48461d06d6bSBaptiste Daroussin else 48561d06d6bSBaptiste Daroussin (*p->endline)(p); 48661d06d6bSBaptiste Daroussin } 48761d06d6bSBaptiste Daroussin 48861d06d6bSBaptiste Daroussin /* Swap current and previous font; for \fP and .ft P */ 48961d06d6bSBaptiste Daroussin void 49061d06d6bSBaptiste Daroussin term_fontlast(struct termp *p) 49161d06d6bSBaptiste Daroussin { 49261d06d6bSBaptiste Daroussin enum termfont f; 49361d06d6bSBaptiste Daroussin 49461d06d6bSBaptiste Daroussin f = p->fontl; 49561d06d6bSBaptiste Daroussin p->fontl = p->fontq[p->fonti]; 49661d06d6bSBaptiste Daroussin p->fontq[p->fonti] = f; 49761d06d6bSBaptiste Daroussin } 49861d06d6bSBaptiste Daroussin 49961d06d6bSBaptiste Daroussin /* Set font, save current, discard previous; for \f, .ft, .B etc. */ 50061d06d6bSBaptiste Daroussin void 50161d06d6bSBaptiste Daroussin term_fontrepl(struct termp *p, enum termfont f) 50261d06d6bSBaptiste Daroussin { 50361d06d6bSBaptiste Daroussin 50461d06d6bSBaptiste Daroussin p->fontl = p->fontq[p->fonti]; 50561d06d6bSBaptiste Daroussin p->fontq[p->fonti] = f; 50661d06d6bSBaptiste Daroussin } 50761d06d6bSBaptiste Daroussin 50861d06d6bSBaptiste Daroussin /* Set font, save previous. */ 50961d06d6bSBaptiste Daroussin void 51061d06d6bSBaptiste Daroussin term_fontpush(struct termp *p, enum termfont f) 51161d06d6bSBaptiste Daroussin { 51261d06d6bSBaptiste Daroussin 51361d06d6bSBaptiste Daroussin p->fontl = p->fontq[p->fonti]; 51461d06d6bSBaptiste Daroussin if (++p->fonti == p->fontsz) { 51561d06d6bSBaptiste Daroussin p->fontsz += 8; 51661d06d6bSBaptiste Daroussin p->fontq = mandoc_reallocarray(p->fontq, 51761d06d6bSBaptiste Daroussin p->fontsz, sizeof(*p->fontq)); 51861d06d6bSBaptiste Daroussin } 51961d06d6bSBaptiste Daroussin p->fontq[p->fonti] = f; 52061d06d6bSBaptiste Daroussin } 52161d06d6bSBaptiste Daroussin 52261d06d6bSBaptiste Daroussin /* Flush to make the saved pointer current again. */ 52361d06d6bSBaptiste Daroussin void 52461d06d6bSBaptiste Daroussin term_fontpopq(struct termp *p, int i) 52561d06d6bSBaptiste Daroussin { 52661d06d6bSBaptiste Daroussin 52761d06d6bSBaptiste Daroussin assert(i >= 0); 52861d06d6bSBaptiste Daroussin if (p->fonti > i) 52961d06d6bSBaptiste Daroussin p->fonti = i; 53061d06d6bSBaptiste Daroussin } 53161d06d6bSBaptiste Daroussin 53261d06d6bSBaptiste Daroussin /* Pop one font off the stack. */ 53361d06d6bSBaptiste Daroussin void 53461d06d6bSBaptiste Daroussin term_fontpop(struct termp *p) 53561d06d6bSBaptiste Daroussin { 53661d06d6bSBaptiste Daroussin 53761d06d6bSBaptiste Daroussin assert(p->fonti); 53861d06d6bSBaptiste Daroussin p->fonti--; 53961d06d6bSBaptiste Daroussin } 54061d06d6bSBaptiste Daroussin 54161d06d6bSBaptiste Daroussin /* 54261d06d6bSBaptiste Daroussin * Handle pwords, partial words, which may be either a single word or a 54361d06d6bSBaptiste Daroussin * phrase that cannot be broken down (such as a literal string). This 54461d06d6bSBaptiste Daroussin * handles word styling. 54561d06d6bSBaptiste Daroussin */ 54661d06d6bSBaptiste Daroussin void 54761d06d6bSBaptiste Daroussin term_word(struct termp *p, const char *word) 54861d06d6bSBaptiste Daroussin { 54961d06d6bSBaptiste Daroussin struct roffsu su; 55061d06d6bSBaptiste Daroussin const char nbrsp[2] = { ASCII_NBRSP, 0 }; 55161d06d6bSBaptiste Daroussin const char *seq, *cp; 55261d06d6bSBaptiste Daroussin int sz, uc; 55361d06d6bSBaptiste Daroussin size_t csz, lsz, ssz; 55461d06d6bSBaptiste Daroussin enum mandoc_esc esc; 55561d06d6bSBaptiste Daroussin 55661d06d6bSBaptiste Daroussin if ((p->flags & TERMP_NOBUF) == 0) { 55761d06d6bSBaptiste Daroussin if ((p->flags & TERMP_NOSPACE) == 0) { 55861d06d6bSBaptiste Daroussin if ((p->flags & TERMP_KEEP) == 0) { 55961d06d6bSBaptiste Daroussin bufferc(p, ' '); 56061d06d6bSBaptiste Daroussin if (p->flags & TERMP_SENTENCE) 56161d06d6bSBaptiste Daroussin bufferc(p, ' '); 56261d06d6bSBaptiste Daroussin } else 56361d06d6bSBaptiste Daroussin bufferc(p, ASCII_NBRSP); 56461d06d6bSBaptiste Daroussin } 56561d06d6bSBaptiste Daroussin if (p->flags & TERMP_PREKEEP) 56661d06d6bSBaptiste Daroussin p->flags |= TERMP_KEEP; 56761d06d6bSBaptiste Daroussin if (p->flags & TERMP_NONOSPACE) 56861d06d6bSBaptiste Daroussin p->flags |= TERMP_NOSPACE; 56961d06d6bSBaptiste Daroussin else 57061d06d6bSBaptiste Daroussin p->flags &= ~TERMP_NOSPACE; 57161d06d6bSBaptiste Daroussin p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); 57261d06d6bSBaptiste Daroussin p->skipvsp = 0; 57361d06d6bSBaptiste Daroussin } 57461d06d6bSBaptiste Daroussin 57561d06d6bSBaptiste Daroussin while ('\0' != *word) { 57661d06d6bSBaptiste Daroussin if ('\\' != *word) { 57761d06d6bSBaptiste Daroussin if (TERMP_NBRWORD & p->flags) { 57861d06d6bSBaptiste Daroussin if (' ' == *word) { 57961d06d6bSBaptiste Daroussin encode(p, nbrsp, 1); 58061d06d6bSBaptiste Daroussin word++; 58161d06d6bSBaptiste Daroussin continue; 58261d06d6bSBaptiste Daroussin } 58361d06d6bSBaptiste Daroussin ssz = strcspn(word, "\\ "); 58461d06d6bSBaptiste Daroussin } else 58561d06d6bSBaptiste Daroussin ssz = strcspn(word, "\\"); 58661d06d6bSBaptiste Daroussin encode(p, word, ssz); 58761d06d6bSBaptiste Daroussin word += (int)ssz; 58861d06d6bSBaptiste Daroussin continue; 58961d06d6bSBaptiste Daroussin } 59061d06d6bSBaptiste Daroussin 59161d06d6bSBaptiste Daroussin word++; 59261d06d6bSBaptiste Daroussin esc = mandoc_escape(&word, &seq, &sz); 59361d06d6bSBaptiste Daroussin switch (esc) { 59461d06d6bSBaptiste Daroussin case ESCAPE_UNICODE: 59561d06d6bSBaptiste Daroussin uc = mchars_num2uc(seq + 1, sz - 1); 59661d06d6bSBaptiste Daroussin break; 59761d06d6bSBaptiste Daroussin case ESCAPE_NUMBERED: 59861d06d6bSBaptiste Daroussin uc = mchars_num2char(seq, sz); 599*c1c95addSBrooks Davis if (uc >= 0) 60061d06d6bSBaptiste Daroussin break; 601*c1c95addSBrooks Davis bufferc(p, ASCII_NBRZW); 602*c1c95addSBrooks Davis continue; 60361d06d6bSBaptiste Daroussin case ESCAPE_SPECIAL: 60461d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) { 60561d06d6bSBaptiste Daroussin cp = mchars_spec2str(seq, sz, &ssz); 60661d06d6bSBaptiste Daroussin if (cp != NULL) 60761d06d6bSBaptiste Daroussin encode(p, cp, ssz); 608*c1c95addSBrooks Davis else 609*c1c95addSBrooks Davis bufferc(p, ASCII_NBRZW); 61061d06d6bSBaptiste Daroussin } else { 61161d06d6bSBaptiste Daroussin uc = mchars_spec2cp(seq, sz); 61261d06d6bSBaptiste Daroussin if (uc > 0) 61361d06d6bSBaptiste Daroussin encode1(p, uc); 614*c1c95addSBrooks Davis else 615*c1c95addSBrooks Davis bufferc(p, ASCII_NBRZW); 61661d06d6bSBaptiste Daroussin } 61761d06d6bSBaptiste Daroussin continue; 6187295610fSBaptiste Daroussin case ESCAPE_UNDEF: 6197295610fSBaptiste Daroussin uc = *seq; 6207295610fSBaptiste Daroussin break; 62161d06d6bSBaptiste Daroussin case ESCAPE_FONTBOLD: 6226d38604fSBaptiste Daroussin case ESCAPE_FONTCB: 62361d06d6bSBaptiste Daroussin term_fontrepl(p, TERMFONT_BOLD); 62461d06d6bSBaptiste Daroussin continue; 62561d06d6bSBaptiste Daroussin case ESCAPE_FONTITALIC: 6266d38604fSBaptiste Daroussin case ESCAPE_FONTCI: 62761d06d6bSBaptiste Daroussin term_fontrepl(p, TERMFONT_UNDER); 62861d06d6bSBaptiste Daroussin continue; 62961d06d6bSBaptiste Daroussin case ESCAPE_FONTBI: 63061d06d6bSBaptiste Daroussin term_fontrepl(p, TERMFONT_BI); 63161d06d6bSBaptiste Daroussin continue; 63261d06d6bSBaptiste Daroussin case ESCAPE_FONT: 6336d38604fSBaptiste Daroussin case ESCAPE_FONTCR: 63461d06d6bSBaptiste Daroussin case ESCAPE_FONTROMAN: 63561d06d6bSBaptiste Daroussin term_fontrepl(p, TERMFONT_NONE); 63661d06d6bSBaptiste Daroussin continue; 63761d06d6bSBaptiste Daroussin case ESCAPE_FONTPREV: 63861d06d6bSBaptiste Daroussin term_fontlast(p); 63961d06d6bSBaptiste Daroussin continue; 64061d06d6bSBaptiste Daroussin case ESCAPE_BREAK: 64161d06d6bSBaptiste Daroussin bufferc(p, '\n'); 64261d06d6bSBaptiste Daroussin continue; 64361d06d6bSBaptiste Daroussin case ESCAPE_NOSPACE: 64461d06d6bSBaptiste Daroussin if (p->flags & TERMP_BACKAFTER) 64561d06d6bSBaptiste Daroussin p->flags &= ~TERMP_BACKAFTER; 64661d06d6bSBaptiste Daroussin else if (*word == '\0') 64761d06d6bSBaptiste Daroussin p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); 64861d06d6bSBaptiste Daroussin continue; 6497295610fSBaptiste Daroussin case ESCAPE_DEVICE: 6507295610fSBaptiste Daroussin if (p->type == TERMTYPE_PDF) 6517295610fSBaptiste Daroussin encode(p, "pdf", 3); 6527295610fSBaptiste Daroussin else if (p->type == TERMTYPE_PS) 6537295610fSBaptiste Daroussin encode(p, "ps", 2); 6547295610fSBaptiste Daroussin else if (p->enc == TERMENC_ASCII) 6557295610fSBaptiste Daroussin encode(p, "ascii", 5); 6567295610fSBaptiste Daroussin else 6577295610fSBaptiste Daroussin encode(p, "utf8", 4); 6587295610fSBaptiste Daroussin continue; 65961d06d6bSBaptiste Daroussin case ESCAPE_HORIZ: 660*c1c95addSBrooks Davis if (p->flags & TERMP_BACKAFTER) { 661*c1c95addSBrooks Davis p->flags &= ~TERMP_BACKAFTER; 662*c1c95addSBrooks Davis continue; 663*c1c95addSBrooks Davis } 66461d06d6bSBaptiste Daroussin if (*seq == '|') { 66561d06d6bSBaptiste Daroussin seq++; 66661d06d6bSBaptiste Daroussin uc = -p->col; 66761d06d6bSBaptiste Daroussin } else 66861d06d6bSBaptiste Daroussin uc = 0; 66961d06d6bSBaptiste Daroussin if (a2roffsu(seq, &su, SCALE_EM) == NULL) 67061d06d6bSBaptiste Daroussin continue; 67161d06d6bSBaptiste Daroussin uc += term_hen(p, &su); 672*c1c95addSBrooks Davis if (uc >= 0) { 673*c1c95addSBrooks Davis while (uc > 0) { 674*c1c95addSBrooks Davis uc -= term_len(p, 1); 675*c1c95addSBrooks Davis if (p->flags & TERMP_BACKBEFORE) 676*c1c95addSBrooks Davis p->flags &= ~TERMP_BACKBEFORE; 677*c1c95addSBrooks Davis else 67861d06d6bSBaptiste Daroussin bufferc(p, ASCII_NBRSP); 679*c1c95addSBrooks Davis } 680*c1c95addSBrooks Davis continue; 681*c1c95addSBrooks Davis } 682*c1c95addSBrooks Davis if (p->flags & TERMP_BACKBEFORE) { 683*c1c95addSBrooks Davis p->flags &= ~TERMP_BACKBEFORE; 684*c1c95addSBrooks Davis assert(p->col > 0); 685*c1c95addSBrooks Davis p->col--; 686*c1c95addSBrooks Davis } 687*c1c95addSBrooks Davis if (p->col >= (size_t)(-uc)) { 68861d06d6bSBaptiste Daroussin p->col += uc; 689*c1c95addSBrooks Davis } else { 69061d06d6bSBaptiste Daroussin uc += p->col; 69161d06d6bSBaptiste Daroussin p->col = 0; 69261d06d6bSBaptiste Daroussin if (p->tcol->offset > (size_t)(-uc)) { 69361d06d6bSBaptiste Daroussin p->ti += uc; 69461d06d6bSBaptiste Daroussin p->tcol->offset += uc; 69561d06d6bSBaptiste Daroussin } else { 69661d06d6bSBaptiste Daroussin p->ti -= p->tcol->offset; 69761d06d6bSBaptiste Daroussin p->tcol->offset = 0; 69861d06d6bSBaptiste Daroussin } 69961d06d6bSBaptiste Daroussin } 70061d06d6bSBaptiste Daroussin continue; 70161d06d6bSBaptiste Daroussin case ESCAPE_HLINE: 70261d06d6bSBaptiste Daroussin if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL) 70361d06d6bSBaptiste Daroussin continue; 70461d06d6bSBaptiste Daroussin uc = term_hen(p, &su); 70561d06d6bSBaptiste Daroussin if (uc <= 0) { 70661d06d6bSBaptiste Daroussin if (p->tcol->rmargin <= p->tcol->offset) 70761d06d6bSBaptiste Daroussin continue; 70861d06d6bSBaptiste Daroussin lsz = p->tcol->rmargin - p->tcol->offset; 70961d06d6bSBaptiste Daroussin } else 71061d06d6bSBaptiste Daroussin lsz = uc; 71161d06d6bSBaptiste Daroussin if (*cp == seq[-1]) 71261d06d6bSBaptiste Daroussin uc = -1; 71361d06d6bSBaptiste Daroussin else if (*cp == '\\') { 71461d06d6bSBaptiste Daroussin seq = cp + 1; 71561d06d6bSBaptiste Daroussin esc = mandoc_escape(&seq, &cp, &sz); 71661d06d6bSBaptiste Daroussin switch (esc) { 71761d06d6bSBaptiste Daroussin case ESCAPE_UNICODE: 71861d06d6bSBaptiste Daroussin uc = mchars_num2uc(cp + 1, sz - 1); 71961d06d6bSBaptiste Daroussin break; 72061d06d6bSBaptiste Daroussin case ESCAPE_NUMBERED: 72161d06d6bSBaptiste Daroussin uc = mchars_num2char(cp, sz); 72261d06d6bSBaptiste Daroussin break; 72361d06d6bSBaptiste Daroussin case ESCAPE_SPECIAL: 72461d06d6bSBaptiste Daroussin uc = mchars_spec2cp(cp, sz); 72561d06d6bSBaptiste Daroussin break; 7267295610fSBaptiste Daroussin case ESCAPE_UNDEF: 7277295610fSBaptiste Daroussin uc = *seq; 7287295610fSBaptiste Daroussin break; 72961d06d6bSBaptiste Daroussin default: 73061d06d6bSBaptiste Daroussin uc = -1; 73161d06d6bSBaptiste Daroussin break; 73261d06d6bSBaptiste Daroussin } 73361d06d6bSBaptiste Daroussin } else 73461d06d6bSBaptiste Daroussin uc = *cp; 73561d06d6bSBaptiste Daroussin if (uc < 0x20 || (uc > 0x7E && uc < 0xA0)) 73661d06d6bSBaptiste Daroussin uc = '_'; 73761d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) { 73861d06d6bSBaptiste Daroussin cp = ascii_uc2str(uc); 73961d06d6bSBaptiste Daroussin csz = term_strlen(p, cp); 74061d06d6bSBaptiste Daroussin ssz = strlen(cp); 74161d06d6bSBaptiste Daroussin } else 74261d06d6bSBaptiste Daroussin csz = (*p->width)(p, uc); 74361d06d6bSBaptiste Daroussin while (lsz >= csz) { 74461d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) 74561d06d6bSBaptiste Daroussin encode(p, cp, ssz); 74661d06d6bSBaptiste Daroussin else 74761d06d6bSBaptiste Daroussin encode1(p, uc); 74861d06d6bSBaptiste Daroussin lsz -= csz; 74961d06d6bSBaptiste Daroussin } 75061d06d6bSBaptiste Daroussin continue; 75161d06d6bSBaptiste Daroussin case ESCAPE_SKIPCHAR: 75261d06d6bSBaptiste Daroussin p->flags |= TERMP_BACKAFTER; 75361d06d6bSBaptiste Daroussin continue; 75461d06d6bSBaptiste Daroussin case ESCAPE_OVERSTRIKE: 75561d06d6bSBaptiste Daroussin cp = seq + sz; 75661d06d6bSBaptiste Daroussin while (seq < cp) { 75761d06d6bSBaptiste Daroussin if (*seq == '\\') { 75861d06d6bSBaptiste Daroussin mandoc_escape(&seq, NULL, NULL); 75961d06d6bSBaptiste Daroussin continue; 76061d06d6bSBaptiste Daroussin } 76161d06d6bSBaptiste Daroussin encode1(p, *seq++); 76261d06d6bSBaptiste Daroussin if (seq < cp) { 76361d06d6bSBaptiste Daroussin if (p->flags & TERMP_BACKBEFORE) 76461d06d6bSBaptiste Daroussin p->flags |= TERMP_BACKAFTER; 76561d06d6bSBaptiste Daroussin else 76661d06d6bSBaptiste Daroussin p->flags |= TERMP_BACKBEFORE; 76761d06d6bSBaptiste Daroussin } 76861d06d6bSBaptiste Daroussin } 76961d06d6bSBaptiste Daroussin /* Trim trailing backspace/blank pair. */ 77061d06d6bSBaptiste Daroussin if (p->tcol->lastcol > 2 && 77161d06d6bSBaptiste Daroussin (p->tcol->buf[p->tcol->lastcol - 1] == ' ' || 77261d06d6bSBaptiste Daroussin p->tcol->buf[p->tcol->lastcol - 1] == '\t')) 77361d06d6bSBaptiste Daroussin p->tcol->lastcol -= 2; 77461d06d6bSBaptiste Daroussin if (p->col > p->tcol->lastcol) 77561d06d6bSBaptiste Daroussin p->col = p->tcol->lastcol; 77661d06d6bSBaptiste Daroussin continue; 777*c1c95addSBrooks Davis case ESCAPE_IGNORE: 778*c1c95addSBrooks Davis bufferc(p, ASCII_NBRZW); 779*c1c95addSBrooks Davis continue; 78061d06d6bSBaptiste Daroussin default: 78161d06d6bSBaptiste Daroussin continue; 78261d06d6bSBaptiste Daroussin } 78361d06d6bSBaptiste Daroussin 78461d06d6bSBaptiste Daroussin /* 78561d06d6bSBaptiste Daroussin * Common handling for Unicode and numbered 78661d06d6bSBaptiste Daroussin * character escape sequences. 78761d06d6bSBaptiste Daroussin */ 78861d06d6bSBaptiste Daroussin 78961d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) { 79061d06d6bSBaptiste Daroussin cp = ascii_uc2str(uc); 79161d06d6bSBaptiste Daroussin encode(p, cp, strlen(cp)); 79261d06d6bSBaptiste Daroussin } else { 79361d06d6bSBaptiste Daroussin if ((uc < 0x20 && uc != 0x09) || 79461d06d6bSBaptiste Daroussin (uc > 0x7E && uc < 0xA0)) 79561d06d6bSBaptiste Daroussin uc = 0xFFFD; 79661d06d6bSBaptiste Daroussin encode1(p, uc); 79761d06d6bSBaptiste Daroussin } 79861d06d6bSBaptiste Daroussin } 79961d06d6bSBaptiste Daroussin p->flags &= ~TERMP_NBRWORD; 80061d06d6bSBaptiste Daroussin } 80161d06d6bSBaptiste Daroussin 80261d06d6bSBaptiste Daroussin static void 80361d06d6bSBaptiste Daroussin adjbuf(struct termp_col *c, size_t sz) 80461d06d6bSBaptiste Daroussin { 80561d06d6bSBaptiste Daroussin if (c->maxcols == 0) 80661d06d6bSBaptiste Daroussin c->maxcols = 1024; 80761d06d6bSBaptiste Daroussin while (c->maxcols <= sz) 80861d06d6bSBaptiste Daroussin c->maxcols <<= 2; 80961d06d6bSBaptiste Daroussin c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf)); 81061d06d6bSBaptiste Daroussin } 81161d06d6bSBaptiste Daroussin 81261d06d6bSBaptiste Daroussin static void 81361d06d6bSBaptiste Daroussin bufferc(struct termp *p, char c) 81461d06d6bSBaptiste Daroussin { 81561d06d6bSBaptiste Daroussin if (p->flags & TERMP_NOBUF) { 81661d06d6bSBaptiste Daroussin (*p->letter)(p, c); 81761d06d6bSBaptiste Daroussin return; 81861d06d6bSBaptiste Daroussin } 81961d06d6bSBaptiste Daroussin if (p->col + 1 >= p->tcol->maxcols) 82061d06d6bSBaptiste Daroussin adjbuf(p->tcol, p->col + 1); 82161d06d6bSBaptiste Daroussin if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) 82261d06d6bSBaptiste Daroussin p->tcol->buf[p->col] = c; 82361d06d6bSBaptiste Daroussin if (p->tcol->lastcol < ++p->col) 82461d06d6bSBaptiste Daroussin p->tcol->lastcol = p->col; 82561d06d6bSBaptiste Daroussin } 82661d06d6bSBaptiste Daroussin 827*c1c95addSBrooks Davis void 828*c1c95addSBrooks Davis term_tab_ref(struct termp *p) 829*c1c95addSBrooks Davis { 830*c1c95addSBrooks Davis if (p->tcol->lastcol && p->tcol->lastcol <= p->col && 831*c1c95addSBrooks Davis (p->flags & TERMP_NOBUF) == 0) 832*c1c95addSBrooks Davis bufferc(p, ASCII_TABREF); 833*c1c95addSBrooks Davis } 834*c1c95addSBrooks Davis 83561d06d6bSBaptiste Daroussin /* 83661d06d6bSBaptiste Daroussin * See encode(). 83761d06d6bSBaptiste Daroussin * Do this for a single (probably unicode) value. 83861d06d6bSBaptiste Daroussin * Does not check for non-decorated glyphs. 83961d06d6bSBaptiste Daroussin */ 84061d06d6bSBaptiste Daroussin static void 84161d06d6bSBaptiste Daroussin encode1(struct termp *p, int c) 84261d06d6bSBaptiste Daroussin { 84361d06d6bSBaptiste Daroussin enum termfont f; 84461d06d6bSBaptiste Daroussin 84561d06d6bSBaptiste Daroussin if (p->flags & TERMP_NOBUF) { 84661d06d6bSBaptiste Daroussin (*p->letter)(p, c); 84761d06d6bSBaptiste Daroussin return; 84861d06d6bSBaptiste Daroussin } 84961d06d6bSBaptiste Daroussin 85061d06d6bSBaptiste Daroussin if (p->col + 7 >= p->tcol->maxcols) 85161d06d6bSBaptiste Daroussin adjbuf(p->tcol, p->col + 7); 85261d06d6bSBaptiste Daroussin 85361d06d6bSBaptiste Daroussin f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ? 85461d06d6bSBaptiste Daroussin p->fontq[p->fonti] : TERMFONT_NONE; 85561d06d6bSBaptiste Daroussin 85661d06d6bSBaptiste Daroussin if (p->flags & TERMP_BACKBEFORE) { 85761d06d6bSBaptiste Daroussin if (p->tcol->buf[p->col - 1] == ' ' || 85861d06d6bSBaptiste Daroussin p->tcol->buf[p->col - 1] == '\t') 85961d06d6bSBaptiste Daroussin p->col--; 86061d06d6bSBaptiste Daroussin else 86161d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = '\b'; 86261d06d6bSBaptiste Daroussin p->flags &= ~TERMP_BACKBEFORE; 86361d06d6bSBaptiste Daroussin } 86461d06d6bSBaptiste Daroussin if (f == TERMFONT_UNDER || f == TERMFONT_BI) { 86561d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = '_'; 86661d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = '\b'; 86761d06d6bSBaptiste Daroussin } 86861d06d6bSBaptiste Daroussin if (f == TERMFONT_BOLD || f == TERMFONT_BI) { 86961d06d6bSBaptiste Daroussin if (c == ASCII_HYPH) 87061d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = '-'; 87161d06d6bSBaptiste Daroussin else 87261d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = c; 87361d06d6bSBaptiste Daroussin p->tcol->buf[p->col++] = '\b'; 87461d06d6bSBaptiste Daroussin } 87561d06d6bSBaptiste Daroussin if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) 87661d06d6bSBaptiste Daroussin p->tcol->buf[p->col] = c; 87761d06d6bSBaptiste Daroussin if (p->tcol->lastcol < ++p->col) 87861d06d6bSBaptiste Daroussin p->tcol->lastcol = p->col; 87961d06d6bSBaptiste Daroussin if (p->flags & TERMP_BACKAFTER) { 88061d06d6bSBaptiste Daroussin p->flags |= TERMP_BACKBEFORE; 88161d06d6bSBaptiste Daroussin p->flags &= ~TERMP_BACKAFTER; 88261d06d6bSBaptiste Daroussin } 88361d06d6bSBaptiste Daroussin } 88461d06d6bSBaptiste Daroussin 88561d06d6bSBaptiste Daroussin static void 88661d06d6bSBaptiste Daroussin encode(struct termp *p, const char *word, size_t sz) 88761d06d6bSBaptiste Daroussin { 88861d06d6bSBaptiste Daroussin size_t i; 88961d06d6bSBaptiste Daroussin 89061d06d6bSBaptiste Daroussin if (p->flags & TERMP_NOBUF) { 89161d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) 89261d06d6bSBaptiste Daroussin (*p->letter)(p, word[i]); 89361d06d6bSBaptiste Daroussin return; 89461d06d6bSBaptiste Daroussin } 89561d06d6bSBaptiste Daroussin 89661d06d6bSBaptiste Daroussin if (p->col + 2 + (sz * 5) >= p->tcol->maxcols) 89761d06d6bSBaptiste Daroussin adjbuf(p->tcol, p->col + 2 + (sz * 5)); 89861d06d6bSBaptiste Daroussin 89961d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) { 90061d06d6bSBaptiste Daroussin if (ASCII_HYPH == word[i] || 90161d06d6bSBaptiste Daroussin isgraph((unsigned char)word[i])) 90261d06d6bSBaptiste Daroussin encode1(p, word[i]); 90361d06d6bSBaptiste Daroussin else { 90461d06d6bSBaptiste Daroussin if (p->tcol->lastcol <= p->col || 90561d06d6bSBaptiste Daroussin (word[i] != ' ' && word[i] != ASCII_NBRSP)) 90661d06d6bSBaptiste Daroussin p->tcol->buf[p->col] = word[i]; 90761d06d6bSBaptiste Daroussin p->col++; 90861d06d6bSBaptiste Daroussin 90961d06d6bSBaptiste Daroussin /* 91061d06d6bSBaptiste Daroussin * Postpone the effect of \z while handling 91161d06d6bSBaptiste Daroussin * an overstrike sequence from ascii_uc2str(). 91261d06d6bSBaptiste Daroussin */ 91361d06d6bSBaptiste Daroussin 91461d06d6bSBaptiste Daroussin if (word[i] == '\b' && 91561d06d6bSBaptiste Daroussin (p->flags & TERMP_BACKBEFORE)) { 91661d06d6bSBaptiste Daroussin p->flags &= ~TERMP_BACKBEFORE; 91761d06d6bSBaptiste Daroussin p->flags |= TERMP_BACKAFTER; 91861d06d6bSBaptiste Daroussin } 91961d06d6bSBaptiste Daroussin } 92061d06d6bSBaptiste Daroussin } 92161d06d6bSBaptiste Daroussin if (p->tcol->lastcol < p->col) 92261d06d6bSBaptiste Daroussin p->tcol->lastcol = p->col; 92361d06d6bSBaptiste Daroussin } 92461d06d6bSBaptiste Daroussin 92561d06d6bSBaptiste Daroussin void 92661d06d6bSBaptiste Daroussin term_setwidth(struct termp *p, const char *wstr) 92761d06d6bSBaptiste Daroussin { 92861d06d6bSBaptiste Daroussin struct roffsu su; 92961d06d6bSBaptiste Daroussin int iop, width; 93061d06d6bSBaptiste Daroussin 93161d06d6bSBaptiste Daroussin iop = 0; 93261d06d6bSBaptiste Daroussin width = 0; 93361d06d6bSBaptiste Daroussin if (NULL != wstr) { 93461d06d6bSBaptiste Daroussin switch (*wstr) { 93561d06d6bSBaptiste Daroussin case '+': 93661d06d6bSBaptiste Daroussin iop = 1; 93761d06d6bSBaptiste Daroussin wstr++; 93861d06d6bSBaptiste Daroussin break; 93961d06d6bSBaptiste Daroussin case '-': 94061d06d6bSBaptiste Daroussin iop = -1; 94161d06d6bSBaptiste Daroussin wstr++; 94261d06d6bSBaptiste Daroussin break; 94361d06d6bSBaptiste Daroussin default: 94461d06d6bSBaptiste Daroussin break; 94561d06d6bSBaptiste Daroussin } 94661d06d6bSBaptiste Daroussin if (a2roffsu(wstr, &su, SCALE_MAX) != NULL) 94761d06d6bSBaptiste Daroussin width = term_hspan(p, &su); 94861d06d6bSBaptiste Daroussin else 94961d06d6bSBaptiste Daroussin iop = 0; 95061d06d6bSBaptiste Daroussin } 95161d06d6bSBaptiste Daroussin (*p->setwidth)(p, iop, width); 95261d06d6bSBaptiste Daroussin } 95361d06d6bSBaptiste Daroussin 95461d06d6bSBaptiste Daroussin size_t 95561d06d6bSBaptiste Daroussin term_len(const struct termp *p, size_t sz) 95661d06d6bSBaptiste Daroussin { 95761d06d6bSBaptiste Daroussin 95861d06d6bSBaptiste Daroussin return (*p->width)(p, ' ') * sz; 95961d06d6bSBaptiste Daroussin } 96061d06d6bSBaptiste Daroussin 96161d06d6bSBaptiste Daroussin static size_t 96261d06d6bSBaptiste Daroussin cond_width(const struct termp *p, int c, int *skip) 96361d06d6bSBaptiste Daroussin { 96461d06d6bSBaptiste Daroussin 96561d06d6bSBaptiste Daroussin if (*skip) { 96661d06d6bSBaptiste Daroussin (*skip) = 0; 96761d06d6bSBaptiste Daroussin return 0; 96861d06d6bSBaptiste Daroussin } else 96961d06d6bSBaptiste Daroussin return (*p->width)(p, c); 97061d06d6bSBaptiste Daroussin } 97161d06d6bSBaptiste Daroussin 97261d06d6bSBaptiste Daroussin size_t 97361d06d6bSBaptiste Daroussin term_strlen(const struct termp *p, const char *cp) 97461d06d6bSBaptiste Daroussin { 97561d06d6bSBaptiste Daroussin size_t sz, rsz, i; 97661d06d6bSBaptiste Daroussin int ssz, skip, uc; 97761d06d6bSBaptiste Daroussin const char *seq, *rhs; 97861d06d6bSBaptiste Daroussin enum mandoc_esc esc; 979*c1c95addSBrooks Davis static const char rej[] = { '\\', ASCII_NBRSP, ASCII_NBRZW, 980*c1c95addSBrooks Davis ASCII_BREAK, ASCII_HYPH, ASCII_TABREF, '\0' }; 98161d06d6bSBaptiste Daroussin 98261d06d6bSBaptiste Daroussin /* 98361d06d6bSBaptiste Daroussin * Account for escaped sequences within string length 98461d06d6bSBaptiste Daroussin * calculations. This follows the logic in term_word() as we 98561d06d6bSBaptiste Daroussin * must calculate the width of produced strings. 98661d06d6bSBaptiste Daroussin */ 98761d06d6bSBaptiste Daroussin 98861d06d6bSBaptiste Daroussin sz = 0; 98961d06d6bSBaptiste Daroussin skip = 0; 99061d06d6bSBaptiste Daroussin while ('\0' != *cp) { 99161d06d6bSBaptiste Daroussin rsz = strcspn(cp, rej); 99261d06d6bSBaptiste Daroussin for (i = 0; i < rsz; i++) 99361d06d6bSBaptiste Daroussin sz += cond_width(p, *cp++, &skip); 99461d06d6bSBaptiste Daroussin 99561d06d6bSBaptiste Daroussin switch (*cp) { 99661d06d6bSBaptiste Daroussin case '\\': 99761d06d6bSBaptiste Daroussin cp++; 99861d06d6bSBaptiste Daroussin rhs = NULL; 9997295610fSBaptiste Daroussin esc = mandoc_escape(&cp, &seq, &ssz); 100061d06d6bSBaptiste Daroussin switch (esc) { 100161d06d6bSBaptiste Daroussin case ESCAPE_UNICODE: 100261d06d6bSBaptiste Daroussin uc = mchars_num2uc(seq + 1, ssz - 1); 100361d06d6bSBaptiste Daroussin break; 100461d06d6bSBaptiste Daroussin case ESCAPE_NUMBERED: 100561d06d6bSBaptiste Daroussin uc = mchars_num2char(seq, ssz); 100661d06d6bSBaptiste Daroussin if (uc < 0) 100761d06d6bSBaptiste Daroussin continue; 100861d06d6bSBaptiste Daroussin break; 100961d06d6bSBaptiste Daroussin case ESCAPE_SPECIAL: 101061d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) { 101161d06d6bSBaptiste Daroussin rhs = mchars_spec2str(seq, ssz, &rsz); 101261d06d6bSBaptiste Daroussin if (rhs != NULL) 101361d06d6bSBaptiste Daroussin break; 101461d06d6bSBaptiste Daroussin } else { 101561d06d6bSBaptiste Daroussin uc = mchars_spec2cp(seq, ssz); 101661d06d6bSBaptiste Daroussin if (uc > 0) 101761d06d6bSBaptiste Daroussin sz += cond_width(p, uc, &skip); 101861d06d6bSBaptiste Daroussin } 101961d06d6bSBaptiste Daroussin continue; 10207295610fSBaptiste Daroussin case ESCAPE_UNDEF: 10217295610fSBaptiste Daroussin uc = *seq; 10227295610fSBaptiste Daroussin break; 10237295610fSBaptiste Daroussin case ESCAPE_DEVICE: 10247295610fSBaptiste Daroussin if (p->type == TERMTYPE_PDF) { 10257295610fSBaptiste Daroussin rhs = "pdf"; 10267295610fSBaptiste Daroussin rsz = 3; 10277295610fSBaptiste Daroussin } else if (p->type == TERMTYPE_PS) { 10287295610fSBaptiste Daroussin rhs = "ps"; 10297295610fSBaptiste Daroussin rsz = 2; 10307295610fSBaptiste Daroussin } else if (p->enc == TERMENC_ASCII) { 10317295610fSBaptiste Daroussin rhs = "ascii"; 10327295610fSBaptiste Daroussin rsz = 5; 10337295610fSBaptiste Daroussin } else { 10347295610fSBaptiste Daroussin rhs = "utf8"; 10357295610fSBaptiste Daroussin rsz = 4; 10367295610fSBaptiste Daroussin } 10377295610fSBaptiste Daroussin break; 103861d06d6bSBaptiste Daroussin case ESCAPE_SKIPCHAR: 103961d06d6bSBaptiste Daroussin skip = 1; 104061d06d6bSBaptiste Daroussin continue; 104161d06d6bSBaptiste Daroussin case ESCAPE_OVERSTRIKE: 104261d06d6bSBaptiste Daroussin rsz = 0; 104361d06d6bSBaptiste Daroussin rhs = seq + ssz; 104461d06d6bSBaptiste Daroussin while (seq < rhs) { 104561d06d6bSBaptiste Daroussin if (*seq == '\\') { 104661d06d6bSBaptiste Daroussin mandoc_escape(&seq, NULL, NULL); 104761d06d6bSBaptiste Daroussin continue; 104861d06d6bSBaptiste Daroussin } 104961d06d6bSBaptiste Daroussin i = (*p->width)(p, *seq++); 105061d06d6bSBaptiste Daroussin if (rsz < i) 105161d06d6bSBaptiste Daroussin rsz = i; 105261d06d6bSBaptiste Daroussin } 105361d06d6bSBaptiste Daroussin sz += rsz; 105461d06d6bSBaptiste Daroussin continue; 105561d06d6bSBaptiste Daroussin default: 105661d06d6bSBaptiste Daroussin continue; 105761d06d6bSBaptiste Daroussin } 105861d06d6bSBaptiste Daroussin 105961d06d6bSBaptiste Daroussin /* 106061d06d6bSBaptiste Daroussin * Common handling for Unicode and numbered 106161d06d6bSBaptiste Daroussin * character escape sequences. 106261d06d6bSBaptiste Daroussin */ 106361d06d6bSBaptiste Daroussin 106461d06d6bSBaptiste Daroussin if (rhs == NULL) { 106561d06d6bSBaptiste Daroussin if (p->enc == TERMENC_ASCII) { 106661d06d6bSBaptiste Daroussin rhs = ascii_uc2str(uc); 106761d06d6bSBaptiste Daroussin rsz = strlen(rhs); 106861d06d6bSBaptiste Daroussin } else { 106961d06d6bSBaptiste Daroussin if ((uc < 0x20 && uc != 0x09) || 107061d06d6bSBaptiste Daroussin (uc > 0x7E && uc < 0xA0)) 107161d06d6bSBaptiste Daroussin uc = 0xFFFD; 107261d06d6bSBaptiste Daroussin sz += cond_width(p, uc, &skip); 107361d06d6bSBaptiste Daroussin continue; 107461d06d6bSBaptiste Daroussin } 107561d06d6bSBaptiste Daroussin } 107661d06d6bSBaptiste Daroussin 107761d06d6bSBaptiste Daroussin if (skip) { 107861d06d6bSBaptiste Daroussin skip = 0; 107961d06d6bSBaptiste Daroussin break; 108061d06d6bSBaptiste Daroussin } 108161d06d6bSBaptiste Daroussin 108261d06d6bSBaptiste Daroussin /* 108361d06d6bSBaptiste Daroussin * Common handling for all escape sequences 108461d06d6bSBaptiste Daroussin * printing more than one character. 108561d06d6bSBaptiste Daroussin */ 108661d06d6bSBaptiste Daroussin 108761d06d6bSBaptiste Daroussin for (i = 0; i < rsz; i++) 108861d06d6bSBaptiste Daroussin sz += (*p->width)(p, *rhs++); 108961d06d6bSBaptiste Daroussin break; 109061d06d6bSBaptiste Daroussin case ASCII_NBRSP: 109161d06d6bSBaptiste Daroussin sz += cond_width(p, ' ', &skip); 109261d06d6bSBaptiste Daroussin cp++; 109361d06d6bSBaptiste Daroussin break; 109461d06d6bSBaptiste Daroussin case ASCII_HYPH: 109561d06d6bSBaptiste Daroussin sz += cond_width(p, '-', &skip); 109661d06d6bSBaptiste Daroussin cp++; 109761d06d6bSBaptiste Daroussin break; 109861d06d6bSBaptiste Daroussin default: 109961d06d6bSBaptiste Daroussin break; 110061d06d6bSBaptiste Daroussin } 110161d06d6bSBaptiste Daroussin } 110261d06d6bSBaptiste Daroussin 110361d06d6bSBaptiste Daroussin return sz; 110461d06d6bSBaptiste Daroussin } 110561d06d6bSBaptiste Daroussin 110661d06d6bSBaptiste Daroussin int 110761d06d6bSBaptiste Daroussin term_vspan(const struct termp *p, const struct roffsu *su) 110861d06d6bSBaptiste Daroussin { 110961d06d6bSBaptiste Daroussin double r; 111061d06d6bSBaptiste Daroussin int ri; 111161d06d6bSBaptiste Daroussin 111261d06d6bSBaptiste Daroussin switch (su->unit) { 111361d06d6bSBaptiste Daroussin case SCALE_BU: 111461d06d6bSBaptiste Daroussin r = su->scale / 40.0; 111561d06d6bSBaptiste Daroussin break; 111661d06d6bSBaptiste Daroussin case SCALE_CM: 111761d06d6bSBaptiste Daroussin r = su->scale * 6.0 / 2.54; 111861d06d6bSBaptiste Daroussin break; 111961d06d6bSBaptiste Daroussin case SCALE_FS: 112061d06d6bSBaptiste Daroussin r = su->scale * 65536.0 / 40.0; 112161d06d6bSBaptiste Daroussin break; 112261d06d6bSBaptiste Daroussin case SCALE_IN: 112361d06d6bSBaptiste Daroussin r = su->scale * 6.0; 112461d06d6bSBaptiste Daroussin break; 112561d06d6bSBaptiste Daroussin case SCALE_MM: 112661d06d6bSBaptiste Daroussin r = su->scale * 0.006; 112761d06d6bSBaptiste Daroussin break; 112861d06d6bSBaptiste Daroussin case SCALE_PC: 112961d06d6bSBaptiste Daroussin r = su->scale; 113061d06d6bSBaptiste Daroussin break; 113161d06d6bSBaptiste Daroussin case SCALE_PT: 113261d06d6bSBaptiste Daroussin r = su->scale / 12.0; 113361d06d6bSBaptiste Daroussin break; 113461d06d6bSBaptiste Daroussin case SCALE_EN: 113561d06d6bSBaptiste Daroussin case SCALE_EM: 113661d06d6bSBaptiste Daroussin r = su->scale * 0.6; 113761d06d6bSBaptiste Daroussin break; 113861d06d6bSBaptiste Daroussin case SCALE_VS: 113961d06d6bSBaptiste Daroussin r = su->scale; 114061d06d6bSBaptiste Daroussin break; 114161d06d6bSBaptiste Daroussin default: 114261d06d6bSBaptiste Daroussin abort(); 114361d06d6bSBaptiste Daroussin } 114461d06d6bSBaptiste Daroussin ri = r > 0.0 ? r + 0.4995 : r - 0.4995; 114561d06d6bSBaptiste Daroussin return ri < 66 ? ri : 1; 114661d06d6bSBaptiste Daroussin } 114761d06d6bSBaptiste Daroussin 114861d06d6bSBaptiste Daroussin /* 114961d06d6bSBaptiste Daroussin * Convert a scaling width to basic units, rounding towards 0. 115061d06d6bSBaptiste Daroussin */ 115161d06d6bSBaptiste Daroussin int 115261d06d6bSBaptiste Daroussin term_hspan(const struct termp *p, const struct roffsu *su) 115361d06d6bSBaptiste Daroussin { 115461d06d6bSBaptiste Daroussin 115561d06d6bSBaptiste Daroussin return (*p->hspan)(p, su); 115661d06d6bSBaptiste Daroussin } 115761d06d6bSBaptiste Daroussin 115861d06d6bSBaptiste Daroussin /* 115961d06d6bSBaptiste Daroussin * Convert a scaling width to basic units, rounding to closest. 116061d06d6bSBaptiste Daroussin */ 116161d06d6bSBaptiste Daroussin int 116261d06d6bSBaptiste Daroussin term_hen(const struct termp *p, const struct roffsu *su) 116361d06d6bSBaptiste Daroussin { 116461d06d6bSBaptiste Daroussin int bu; 116561d06d6bSBaptiste Daroussin 116661d06d6bSBaptiste Daroussin if ((bu = (*p->hspan)(p, su)) >= 0) 116761d06d6bSBaptiste Daroussin return (bu + 11) / 24; 116861d06d6bSBaptiste Daroussin else 116961d06d6bSBaptiste Daroussin return -((-bu + 11) / 24); 117061d06d6bSBaptiste Daroussin } 1171