Fix a couple of bugs in tty_draw_line: do not loop forever if orphan

padding appears, or if a wide character is trimmed at the right of the
region. Much help with testing from qingliu at alauda dot io in GitHub
issue 5024.
This commit is contained in:
nicm
2026-05-13 13:12:23 +00:00
parent dbc0710bc9
commit 281e8ff766

View File

@@ -29,7 +29,6 @@ enum tty_draw_line_state {
TTY_DRAW_LINE_NEW2, TTY_DRAW_LINE_NEW2,
TTY_DRAW_LINE_EMPTY, TTY_DRAW_LINE_EMPTY,
TTY_DRAW_LINE_SAME, TTY_DRAW_LINE_SAME,
TTY_DRAW_LINE_PAD,
TTY_DRAW_LINE_DONE TTY_DRAW_LINE_DONE
}; };
static const char* tty_draw_line_states[] = { static const char* tty_draw_line_states[] = {
@@ -39,7 +38,6 @@ static const char* tty_draw_line_states[] = {
"NEW2", "NEW2",
"EMPTY", "EMPTY",
"SAME", "SAME",
"PAD",
"DONE" "DONE"
}; };
@@ -100,25 +98,6 @@ tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
} }
} }
/* Is this cell empty? */
static u_int
tty_draw_line_get_empty(const struct grid_cell *gc, u_int nx)
{
u_int empty = 0;
if (gc->data.width != 1 && gc->data.width > nx)
empty = nx;
else if (gc->attr == 0 && gc->link == 0) {
if (gc->flags & GRID_FLAG_CLEARED)
empty = 1;
else if (gc->flags & GRID_FLAG_TAB)
empty = gc->data.width;
else if (gc->data.size == 1 && *gc->data.data == ' ')
empty = 1;
}
return (empty);
}
/* Draw a line from screen to tty. */ /* Draw a line from screen to tty. */
void void
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
@@ -239,6 +218,9 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
next_state = TTY_DRAW_LINE_DONE; next_state = TTY_DRAW_LINE_DONE;
gcp = &grid_default_cell; gcp = &grid_default_cell;
} else { } else {
if (i > nx)
fatalx("position %u > width %u", i, nx);
/* Get the current cell. */ /* Get the current cell. */
grid_view_get_cell(gd, px + i, py, &gc); grid_view_get_cell(gd, px + i, py, &gc);
@@ -253,20 +235,36 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
} }
/* Work out the the empty width. */ /* Work out the the empty width. */
if (px >= ex || i >= ex - px) empty = 0;
if (px >= ex || i >= ex - px) {
/* Outside the area being drawn. */
empty = 1; empty = 1;
else if (gcp->bg != last.bg) } else if (gcp->data.width > nx - i) {
empty = 0; /* Wide character that has been truncated. */
else empty = nx - i;
empty = tty_draw_line_get_empty(gcp, nx - i); } else if (gcp->flags & GRID_FLAG_PADDING) {
/* Orphan padding cell. */
empty = 1;
} else if (gcp->bg == last.bg && gcp->attr == 0 &&
gcp->link == 0) {
/*
* No attributes - empty if cleared, tab or
* space.
*/
if (gcp->flags & GRID_FLAG_CLEARED)
empty = 1;
else if (gcp->flags & GRID_FLAG_TAB)
empty = gcp->data.width;
else if (gcp->data.size == 1 &&
*gcp->data.data == ' ')
empty = 1;
}
/* Work out the next state. */ /* Work out the next state. */
if (empty != 0) if (empty != 0)
next_state = TTY_DRAW_LINE_EMPTY; next_state = TTY_DRAW_LINE_EMPTY;
else if (current_state == TTY_DRAW_LINE_FIRST) else if (current_state == TTY_DRAW_LINE_FIRST)
next_state = TTY_DRAW_LINE_SAME; next_state = TTY_DRAW_LINE_SAME;
else if (gcp->flags & GRID_FLAG_PADDING)
next_state = TTY_DRAW_LINE_PAD;
else if (grid_cells_look_equal(gcp, &last)) { else if (grid_cells_look_equal(gcp, &last)) {
if (gcp->data.size > (sizeof buf) - len) if (gcp->data.size > (sizeof buf) - len)
next_state = TTY_DRAW_LINE_FLUSH; next_state = TTY_DRAW_LINE_FLUSH;
@@ -312,8 +310,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
} }
/* Append the cell if it is not empty and not padding. */ /* Append the cell if it is not empty and not padding. */
if (next_state != TTY_DRAW_LINE_EMPTY && if (next_state != TTY_DRAW_LINE_EMPTY) {
next_state != TTY_DRAW_LINE_PAD) {
memcpy(buf + len, gcp->data.data, gcp->data.size); memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size; len += gcp->data.size;
width += gcp->data.width; width += gcp->data.width;