summaryrefslogtreecommitdiff
path: root/src/selection.c
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-15 12:42:11 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-15 12:42:11 +0300
commit6d2dc774947787dcef73a3251d1e9ec51bc526df (patch)
tree05f3c67039a9c6e69854e7de88ef9d40c110ec21 /src/selection.c
parent7b31d6a05357953c035e2ab05ec117ef748f7798 (diff)
Report selection allocation failure instead of truncating
On a screen taller than 256 rows the selection's row list is allocated. When that failed, it fell back to the 256-entry stack array, so the selection and the snapshot's selection spans silently stopped at row 256 while every call still reported success. lines_init() now reports the failure. term_select_start() and term_select_extend() return false and leave the selection unchanged, term_snapshot_update() fails and keeps its previous content, and the text getters return NULL, as they do for their other allocations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/selection.c')
-rw-r--r--src/selection.c77
1 files changed, 57 insertions, 20 deletions
diff --git a/src/selection.c b/src/selection.c
index 40fb1f1..1d931c9 100644
--- a/src/selection.c
+++ b/src/selection.c
@@ -109,7 +109,9 @@ struct lines {
struct line_pos stack[256];
};
-static void lines_init(struct lines *L, const struct term *t)
+/* False when a screen taller than the stack array needs memory and none is
+ * left. L can still be passed to lines_free(). */
+static bool lines_init(struct lines *L, const struct term *t)
{
L->t = t;
L->cols = t->cols;
@@ -117,14 +119,16 @@ static void lines_init(struct lines *L, const struct term *t)
L->hist_end = L->hist ? hist_end_id(&t->hist) : 0;
L->first = first_retained_line(t);
L->pos = t->rows <= 256 ? L->stack : malloc((size_t)t->rows * sizeof(*L->pos));
- if (!L->pos)
+ if (!L->pos) {
L->pos = L->stack;
- int rows = L->pos == L->stack && t->rows > 256 ? 256 : t->rows;
+ return false;
+ }
struct line_pos p = screen_top_line(t);
- for (int y = 0; y < rows; y++) {
+ for (int y = 0; y < t->rows; y++) {
L->pos[y] = p;
p = line_pos_next(p, screen_get_row(&t->scr, y));
}
+ return true;
}
static void lines_free(struct lines *L)
@@ -135,7 +139,7 @@ static void lines_free(struct lines *L)
static inline int lines_rows(const struct lines *L)
{
- return L->pos == L->stack && L->t->rows > 256 ? 256 : L->t->rows;
+ return L->t->rows;
}
static inline bool in_hist(const struct lines *L, uint64_t id)
@@ -498,27 +502,48 @@ static void select_range(struct term *t, const struct lines *L,
t->selection = true;
}
-void term_select_start(struct term *t, struct term_vector2i cell, bool right_half,
+bool term_select_start(struct term *t, struct term_vector2i cell, bool right_half,
enum term_select_unit unit)
{
- if (!t) return;
+ if (!t) return false;
term_write_begin(t);
struct lines L;
- lines_init(&L, t);
+ if (!lines_init(&L, t)) {
+ lines_free(&L);
+ term_write_end(t);
+ return false;
+ }
+ struct term_sel_point first, last;
+ unit_span(&L, cell.x, cell.y, right_half, unit, &first, &last);
+ lines_free(&L);
+ enum term_select_unit old_unit = t->select_unit;
+ struct term_sel_point old_first = t->sel_anchor_first, old_last = t->sel_anchor_last;
+ bool old_selection = t->selection;
t->select_unit = unit;
- unit_span(&L, cell.x, cell.y, right_half, unit, &t->sel_anchor_first, &t->sel_anchor_last);
+ t->sel_anchor_first = first;
+ t->sel_anchor_last = last;
t->selection = false;
- lines_free(&L);
- term_select_extend(t, cell, right_half);
+ bool ok = term_select_extend(t, cell, right_half);
+ if (!ok) {
+ t->select_unit = old_unit;
+ t->sel_anchor_first = old_first;
+ t->sel_anchor_last = old_last;
+ t->selection = old_selection;
+ }
term_write_end(t);
+ return ok;
}
-void term_select_extend(struct term *t, struct term_vector2i cell, bool right_half)
+bool term_select_extend(struct term *t, struct term_vector2i cell, bool right_half)
{
- if (!t) return;
+ if (!t) return false;
term_write_begin(t);
struct lines L;
- lines_init(&L, t);
+ if (!lines_init(&L, t)) {
+ lines_free(&L);
+ term_write_end(t);
+ return false;
+ }
struct term_sel_point first, last;
unit_span(&L, cell.x, cell.y, right_half, t->select_unit, &first, &last);
if (sel_point_cmp(first, t->sel_anchor_first) > 0) first = t->sel_anchor_first;
@@ -529,6 +554,7 @@ void term_select_extend(struct term *t, struct term_vector2i cell, bool right_ha
select_range(t, &L, first, sel_point_prev(&L, last));
lines_free(&L);
term_write_end(t);
+ return true;
}
bool term_has_selection(const struct term *t)
@@ -541,7 +567,7 @@ bool term_has_selection(const struct term *t)
return has;
}
-void term_selection_view_spans(const struct term *t, int *c0, int *c1)
+bool term_selection_view_spans(const struct term *t, int *c0, int *c1)
{
int rows = t->rows, cols = t->cols;
for (int y = 0; y < rows; y++) {
@@ -549,13 +575,16 @@ void term_selection_view_spans(const struct term *t, int *c0, int *c1)
c1[y] = -1;
}
if (!t->selection)
- return;
+ return true;
struct lines L;
- lines_init(&L, t);
+ if (!lines_init(&L, t)) {
+ lines_free(&L);
+ return false;
+ }
struct term_sel_point start, end;
if (!selection_bounds(&L, &start, &end)) {
lines_free(&L);
- return;
+ return true;
}
uint64_t prev_id = UINT64_MAX;
int prev_seg = -1;
@@ -596,6 +625,7 @@ void term_selection_view_spans(const struct term *t, int *c0, int *c1)
c1[y] = (int)e;
}
lines_free(&L);
+ return true;
}
static bool text_reserve(char **text, size_t *cap, size_t need)
@@ -619,7 +649,10 @@ static char *range_text_inner(const struct term *t,
{
if (!t || (!bounds && !t->selection)) return NULL;
struct lines L;
- lines_init(&L, t);
+ if (!lines_init(&L, t)) {
+ lines_free(&L);
+ return NULL;
+ }
struct term_sel_point start, end;
if (bounds) {
start = bounds[0];
@@ -783,7 +816,11 @@ char *term_history_text(struct term *t, int max_rows)
}
int begin_row = end_row > max_rows ? end_row - max_rows : 0;
struct lines L;
- lines_init(&L, t);
+ if (!lines_init(&L, t)) {
+ lines_free(&L);
+ term_write_end(t);
+ return NULL;
+ }
struct term_sel_point bounds[2] = {0};
int first_seg, last_seg;
char *text = NULL;