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
term_setcol(struct termp * p,size_t maxtcol)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
term_free(struct termp * p)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
term_begin(struct termp * p,term_margin head,term_margin foot,const struct roff_meta * arg)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
term_end(struct termp * p)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
term_flushln(struct termp * p)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
term_fill(struct termp * p,size_t * nbr,size_t * vbr,size_t vtarget)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
term_field(struct termp * p,size_t vbl,size_t nbr)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
endline(struct termp * p)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
term_newln(struct termp * p)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
term_vspace(struct termp * p)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
term_fontlast(struct termp * p)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
term_fontrepl(struct termp * p,enum termfont f)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
term_fontpush(struct termp * p,enum termfont f)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
term_fontpopq(struct termp * p,int i)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
term_fontpop(struct termp * p)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
term_word(struct termp * p,const char * word)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
adjbuf(struct termp_col * c,size_t sz)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
bufferc(struct termp * p,char c)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
term_tab_ref(struct termp * p)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
encode1(struct termp * p,int c)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
encode(struct termp * p,const char * word,size_t sz)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
term_setwidth(struct termp * p,const char * wstr)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
term_len(const struct termp * p,size_t sz)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
cond_width(const struct termp * p,int c,int * skip)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
term_strlen(const struct termp * p,const char * cp)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
term_vspan(const struct termp * p,const struct roffsu * su)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
term_hspan(const struct termp * p,const struct roffsu * su)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
term_hen(const struct termp * p,const struct roffsu * su)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