aboutsummaryrefslogtreecommitdiff
path: root/contrib/mandoc/tbl_term.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/mandoc/tbl_term.c')
-rw-r--r--contrib/mandoc/tbl_term.c287
1 files changed, 165 insertions, 122 deletions
diff --git a/contrib/mandoc/tbl_term.c b/contrib/mandoc/tbl_term.c
index e92349514d9f..a7f057084266 100644
--- a/contrib/mandoc/tbl_term.c
+++ b/contrib/mandoc/tbl_term.c
@@ -1,6 +1,6 @@
-/* $Id: tbl_term.c,v 1.79 2022/08/28 10:58:31 schwarze Exp $ */
+/* $Id: tbl_term.c,v 1.81 2025/07/24 17:54:48 schwarze Exp $ */
/*
- * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -39,23 +39,23 @@
static size_t term_tbl_len(size_t, void *);
static size_t term_tbl_strlen(const char *, void *);
-static size_t term_tbl_sulen(const struct roffsu *, void *);
static void tbl_data(struct termp *, const struct tbl_opts *,
const struct tbl_cell *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_direct_border(struct termp *, int, size_t);
-static void tbl_fill_border(struct termp *, int, size_t);
-static void tbl_fill_char(struct termp *, char, size_t);
-static void tbl_fill_string(struct termp *, const char *, size_t);
+static void tbl_fill_border(struct termp *, int, size_t *, size_t);
+static void tbl_fill_char(struct termp *, char, size_t *, size_t);
+static void tbl_fill_string(struct termp *, const char *,
+ size_t *, size_t);
static void tbl_hrule(struct termp *, const struct tbl_span *,
const struct tbl_span *, const struct tbl_span *,
int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_number(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_word(struct termp *, const struct tbl_dat *);
@@ -140,15 +140,6 @@ static const int *borders_locale;
static size_t
-term_tbl_sulen(const struct roffsu *su, void *arg)
-{
- int i;
-
- i = term_hen((const struct termp *)arg, su);
- return i > 0 ? i : 0;
-}
-
-static size_t
term_tbl_strlen(const char *p, void *arg)
{
return term_strlen((const struct termp *)arg, p);
@@ -166,16 +157,29 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_cell *cp, *cpn, *cpp, *cps;
const struct tbl_dat *dp;
- static size_t offset;
- size_t save_offset;
- size_t coloff, tsz;
- int hspans, ic, more;
- int dvert, fc, horiz, lhori, rhori, uvert;
+
+ /* Positions and widths in basic units. */
+ static size_t offset; /* Of the table as a whole. */
+ size_t save_offset; /* Of the surrounding text. */
+ size_t coloff; /* Of this cell. */
+ size_t tsz; /* Total width of the table. */
+ size_t enw; /* Width of one EN unit. */
+
+ int ic; /* Column number. */
+ int hspans; /* Number of spans following this cell. */
+ int horiz; /* Boolean: this row only contains a line. */
+ int lhori; /* Number of horizontal lines pointing left. */
+ int rhori; /* Number of horizontal lines pointing right. */
+ int dvert; /* Number of vertical lines pointing down. */
+ int uvert; /* Number of vertical lines pointing up. */
+ int fc; /* Frame character index in borders_locale[]. */
+ int more; /* Boolean: there are more columns to print. */
/* Inhibit printing of spaces: we do padding ourselves. */
tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
save_offset = tp->tcol->offset;
+ enw = term_len(tp, 1);
/*
* The first time we're invoked for a given table block,
@@ -188,7 +192,6 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tbl.len = term_tbl_len;
tp->tbl.slen = term_tbl_strlen;
- tp->tbl.sulen = term_tbl_sulen;
tp->tbl.arg = tp;
tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
@@ -197,17 +200,36 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
offset = tp->tcol->offset;
if (sp->opts->opts & TBL_OPT_CENTRE) {
- tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
- ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
+
+ /*
+ * Vertical lines on the edges of the table make the
+ * table wider; take that into account for centering.
+ * The following assignment essentially says that a
+ * line on the right side occupies two columns (which
+ * matches reality) and a line on the left side three
+ * columns (which does not match reality; in fact,
+ * it only occupies two columns). But this is how
+ * groff does centering, so for compatibility, use
+ * the same numbers as groff.
+ */
+
+ tsz = term_len(tp,
+ sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
+ 5 : 3 * !!sp->opts->lvert + 2 * !!sp->opts->rvert);
+
+ /* Column widths and column spacing. */
+
for (ic = 0; ic + 1 < sp->opts->cols; ic++)
tsz += tp->tbl.cols[ic].width +
- tp->tbl.cols[ic].spacing;
+ term_len(tp, tp->tbl.cols[ic].spacing);
if (sp->opts->cols)
tsz += tp->tbl.cols[sp->opts->cols - 1].width;
+
if (offset + tsz > tp->tcol->rmargin)
- tsz -= 1;
+ tsz -= enw;
offset = offset + tp->tcol->rmargin > tsz ?
- (offset + tp->tcol->rmargin - tsz) / 2 : 0;
+ ((offset + tp->tcol->rmargin - tsz) / enw / 2) *
+ enw : 0;
tp->tcol->offset = offset;
}
@@ -239,7 +261,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
sp->opts->lvert)
- coloff++;
+ coloff += enw * 2;
tp->tcol->rmargin = coloff;
/* Set up the data columns. */
@@ -254,7 +276,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
coloff += tp->tbl.cols[ic].width;
tp->tcol->rmargin = coloff;
if (ic + 1 < sp->opts->cols)
- coloff += tp->tbl.cols[ic].spacing;
+ coloff += term_len(tp,
+ tp->tbl.cols[ic].spacing);
if (hspans) {
hspans--;
continue;
@@ -269,7 +292,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Set up a column for a right vertical frame. */
tp->tcol++;
- tp->tcol->offset = coloff + 1;
+ tp->tcol->offset = coloff + enw;
tp->tcol->rmargin = tp->maxrmargin;
/* Spans may have reduced the number of columns. */
@@ -279,6 +302,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Fill the buffers for all data columns. */
tp->tcol = tp->tcols;
+ coloff = tp->tcols[1].offset;
cp = cpn = sp->layout->first;
dp = sp->first;
hspans = 0;
@@ -294,7 +318,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
tp->col = 0;
tp->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE);
- tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
+ tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic,
+ &coloff);
+ coloff += term_len(tp, tp->tbl.cols[ic].spacing);
if (dp != NULL &&
(ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
hspans = dp->hspans;
@@ -328,8 +354,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcol->offset;
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
+ tbl_direct_border(tp, BHORIZ * rhori, enw);
}
/* Print the data cells. */
@@ -348,6 +374,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
for (ic = 0; ic < sp->opts->cols; ic++) {
/*
+ * Handle horizontal alignment.
* Figure out whether to print a
* vertical line after this cell
* and advance to next layout cell.
@@ -362,6 +389,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->pos == TBL_SPAN_DATA)
uvert = dvert = cps->vert;
switch (cp->pos) {
+ case TBL_CELL_CENTRE:
+ tp->flags |= TERMP_CENTER;
+ break;
+ case TBL_CELL_RIGHT:
+ tp->flags |= TERMP_RIGHT;
+ break;
+ case TBL_CELL_LONG:
+ if (hspans == 0)
+ tp->tcol->offset += enw;
+ break;
case TBL_CELL_HORIZ:
fc = BHORIZ;
break;
@@ -432,6 +469,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
if (tp->tcol->col < tp->tcol->lastcol)
term_flushln(tp);
+ tp->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
+ if (cp != NULL && cp->pos == TBL_CELL_LONG)
+ tp->tcol->offset -= enw;
if (tp->tcol->col < tp->tcol->lastcol)
more = 1;
@@ -451,15 +491,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
continue;
}
- if (tp->viscol < tp->tcol->rmargin) {
- (*tp->advance)(tp, tp->tcol->rmargin
- - tp->viscol);
- tp->viscol = tp->tcol->rmargin;
- }
+ if (tp->viscol < tp->tcol->rmargin)
+ (*tp->advance)(tp,
+ tp->tcol->rmargin - tp->viscol);
while (tp->viscol < tp->tcol->rmargin +
- tp->tbl.cols[ic].spacing / 2)
+ term_len(tp, tp->tbl.cols[ic].spacing / 2))
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
+ BHORIZ * lhori, enw);
if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
continue;
@@ -479,7 +517,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (tp->tbl.cols[ic].spacing)
tbl_direct_border(tp,
BLEFT * lhori + BRIGHT * rhori +
- BUP * uvert + BDOWN * dvert, 1);
+ BUP * uvert + BDOWN * dvert, enw);
if (tp->enc == TERMENC_UTF8)
uvert = dvert = 0;
@@ -489,7 +527,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tbl_direct_border(tp,
BHORIZ * rhori +
BUP * (uvert > 1) +
- BDOWN * (dvert > 1), 1);
+ BDOWN * (dvert > 1), enw);
}
}
@@ -528,15 +566,14 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
sp->layout->last->col + 1 < sp->opts->cols)) {
tp->tcol++;
- do {
+ if (tp->tcol->offset > tp->viscol)
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
- } while (tp->viscol < tp->tcol->offset);
+ BHORIZ * lhori,
+ tp->tcol->offset - tp->viscol);
}
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
}
(*tp->endline)(tp);
- tp->viscol = 0;
} while (more);
/*
@@ -575,6 +612,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
const struct tbl_cell *cpn; /* Layout cell below this line. */
const struct tbl_dat *dpn; /* Data cell below this line. */
const struct roffcol *col; /* Contains width and spacing. */
+ size_t enw; /* Width of one EN unit. */
int opts; /* For the table as a whole. */
int bw; /* Box line width. */
int hw; /* Horizontal line width. */
@@ -599,16 +637,19 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
/* Print the left end of the line. */
- if (tp->viscol == 0) {
+ enw = term_len(tp, 1);
+ if (tp->viscol == 0)
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcols->offset;
- }
- if (flags != 0)
+ if (flags != 0) {
tbl_direct_border(tp,
(spp == NULL ? 0 : BUP * bw) +
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || cpn == NULL ||
- cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
+ cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), enw);
+ tbl_direct_border(tp,
+ (spp == NULL || cpn == NULL ||
+ cpn->pos != TBL_CELL_DOWN ? BHORIZ * hw : 0), enw);
+ }
col = tp->tbl.cols;
for (;;) {
@@ -625,7 +666,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
strcmp(dpn->string, "\\^") != 0))
? hw : 0;
tbl_direct_border(tp, BHORIZ * lw,
- col->width + col->spacing / 2);
+ col->width + term_len(tp, col->spacing / 2));
/*
* Figure out whether a vertical line is crossing
@@ -678,7 +719,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
if (col->spacing)
tbl_direct_border(tp, BLEFT * lw +
- BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
+ BRIGHT * rw + BUP * uw + BDOWN * dw, enw);
/*
* In ASCII output, a crossing may print two characters.
@@ -688,13 +729,14 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
uw = dw = 0;
if (col->spacing > 2)
tbl_direct_border(tp,
- BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
+ BHORIZ * rw + BUP * uw + BDOWN * dw, enw);
/* Padding before the start of the next column. */
if (col->spacing > 4)
tbl_direct_border(tp,
- BHORIZ * rw, (col->spacing - 3) / 2);
+ BHORIZ * rw,
+ term_len(tp, (col->spacing - 3) / 2));
}
/* Print the right end of the line. */
@@ -705,23 +747,22 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || spn == NULL ||
spn->layout->last->pos != TBL_CELL_DOWN ?
- BLEFT * hw : 0), 1);
+ BLEFT * hw : 0), enw);
(*tp->endline)(tp);
- tp->viscol = 0;
}
}
static void
tbl_data(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_cell *cp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
switch (cp->pos) {
case TBL_CELL_HORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_CELL_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -735,11 +776,11 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
return;
case TBL_DATA_HORIZ:
case TBL_DATA_NHORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_DATA_NDHORIZ:
case TBL_DATA_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -750,10 +791,10 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
case TBL_CELL_CENTRE:
case TBL_CELL_LEFT:
case TBL_CELL_RIGHT:
- tbl_literal(tp, dp, col);
+ tbl_literal(tp, dp, col, coloff);
break;
case TBL_CELL_NUMBER:
- tbl_number(tp, opts, dp, col);
+ tbl_number(tp, opts, dp, col, coloff);
break;
case TBL_CELL_DOWN:
case TBL_CELL_SPAN:
@@ -763,46 +804,72 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
}
}
+/*
+ * Print multiple copies of the string cp to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_string(struct termp *tp, const char *cp, size_t len)
+tbl_fill_string(struct termp *tp, const char *cp, size_t *coloff, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the string cp in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
+ if (len == 0)
+ return;
sz = term_strlen(tp, cp);
- for (i = 0; i < len; i += sz)
+ target = tp->tcol->offset + len;
+ while (*coloff < target) {
term_word(tp, cp);
+ *coloff += sz;
+ }
}
+/*
+ * Print multiple copies of the ASCII character c to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_char(struct termp *tp, char c, size_t len)
+tbl_fill_char(struct termp *tp, char c, size_t *coloff, size_t len)
{
char cp[2];
cp[0] = c;
cp[1] = '\0';
- tbl_fill_string(tp, cp, len);
+ tbl_fill_string(tp, cp, coloff, len);
}
+/*
+ * Print multiple copies of the border c to fill len basic units.
+ * Used for horizontal lines inside table cells.
+ */
static void
-tbl_fill_border(struct termp *tp, int c, size_t len)
+tbl_fill_border(struct termp *tp, int c, size_t *coloff, size_t len)
{
char buf[12];
if ((c = borders_locale[c]) > 127) {
(void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
- tbl_fill_string(tp, buf, len);
+ tbl_fill_string(tp, buf, coloff, len);
} else
- tbl_fill_char(tp, c, len);
+ tbl_fill_char(tp, c, coloff, len);
}
+/*
+ * The same, but bypassing term_flushln().
+ * Used for horizontal and vertical lines at the edges of table cells.
+ */
static void
tbl_direct_border(struct termp *tp, int c, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the character in basic units. */
+ size_t enw2; /* Width of half an EN in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
c = borders_locale[c];
- sz = (*tp->width)(tp, c);
- for (i = 0; i < len; i += sz) {
+ sz = (*tp->getwidth)(tp, c);
+ enw2 = (*tp->getwidth)(tp, ' ') / 2;
+ target = tp->viscol + len;
+ while (tp->viscol + enw2 < target) {
(*tp->letter)(tp, c);
tp->viscol += sz;
}
@@ -810,56 +877,36 @@ tbl_direct_border(struct termp *tp, int c, size_t len)
static void
tbl_literal(struct termp *tp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
- size_t len, padl, padr, width;
- int ic, hspans;
+ size_t width; /* Of the cell including following spans [BU]. */
+ int ic; /* Column number of the cell. */
+ int hspans; /* Number of horizontal spans that follow. */
- assert(dp->string);
- len = term_strlen(tp, dp->string);
width = col->width;
ic = dp->layout->col;
hspans = dp->hspans;
while (hspans--) {
- width += tp->tbl.cols[ic].spacing;
+ width += term_len(tp, tp->tbl.cols[ic].spacing);
ic++;
width += tp->tbl.cols[ic].width;
}
-
- padr = width > len ? width - len : 0;
- padl = 0;
-
- switch (dp->layout->pos) {
- case TBL_CELL_LONG:
- padl = term_len(tp, 1);
- padr = padr > padl ? padr - padl : 0;
- break;
- case TBL_CELL_CENTRE:
- if (2 > padr)
- break;
- padl = padr / 2;
- padr -= padl;
- break;
- case TBL_CELL_RIGHT:
- padl = padr;
- padr = 0;
- break;
- default:
- break;
- }
-
- tbl_fill_char(tp, ASCII_NBRSP, padl);
tbl_word(tp, dp);
- tbl_fill_char(tp, ASCII_NBRSP, padr);
+ *coloff += width;
}
static void
tbl_number(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
const char *cp, *lastdigit, *lastpoint;
- size_t intsz, padl, totsz;
+
+ /* Widths in basic units. */
+ size_t pad; /* Padding before the number. */
+ size_t totsz; /* Of the number to be printed. */
+ size_t intsz; /* Of the integer part. */
+
char buf[2];
/*
@@ -883,7 +930,7 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
/* Then measure both widths. */
- padl = 0;
+ pad = 0;
totsz = term_strlen(tp, dp->string);
if (lastdigit != NULL) {
if (lastpoint == NULL)
@@ -901,23 +948,19 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
*/
if (col->decimal > intsz && col->width > totsz) {
- padl = col->decimal - intsz;
- if (padl + totsz > col->width)
- padl = col->width - totsz;
+ pad = col->decimal - intsz;
+ if (pad + totsz > col->width)
+ pad = col->width - totsz;
}
/* If it is not a number, simply center the string. */
} else if (col->width > totsz)
- padl = (col->width - totsz) / 2;
+ pad = (col->width - totsz) / 2;
- tbl_fill_char(tp, ASCII_NBRSP, padl);
+ tbl_fill_char(tp, ASCII_NBRSP, coloff, pad);
tbl_word(tp, dp);
-
- /* Pad right to fill the column. */
-
- if (col->width > padl + totsz)
- tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
+ *coloff += col->width;
}
static void