string-util: add upper bound to ellipsize_mem UTF-8 walks
Validate UTF-8 characters against the caller-provided byte slice.
This stops truncated sequences from borrowing bytes past old_length.
Follow-up for e69cc9eb36
This commit is contained in:
+49
-10
@@ -357,6 +357,33 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Walk backwards from 'end' (exclusive) to the start of the last complete UTF-8 character, without
|
||||
* descending below 'start', validate it and return a pointer to its first byte, optionally decoding it
|
||||
* into *ret_c. Returns NULL on a truncated or otherwise malformed sequence. */
|
||||
static const char* find_previous_unichar(const char *start, const char *end, char32_t *ret_c) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(start);
|
||||
assert(end);
|
||||
assert(end > start);
|
||||
|
||||
for (p = end; p > start; ) {
|
||||
p--;
|
||||
if (((uint8_t) *p & 0xc0) != 0x80) /* Found a non-continuation byte, i.e. a character start. */
|
||||
break;
|
||||
}
|
||||
|
||||
r = utf8_encoded_valid_unichar(p, end - p);
|
||||
if (r < 0 || p + r != end)
|
||||
return NULL;
|
||||
|
||||
if (ret_c)
|
||||
assert_se(utf8_encoded_to_unichar(p, ret_c) == r);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
|
||||
size_t x, k, len, len2;
|
||||
const char *i, *j;
|
||||
@@ -400,11 +427,13 @@ char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
|
||||
continue; /* ANSI sequences don't take up any space in output */
|
||||
}
|
||||
|
||||
char32_t c;
|
||||
r = utf8_encoded_to_unichar(i, &c);
|
||||
r = utf8_encoded_valid_unichar(i, s + old_length - i);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
char32_t c;
|
||||
assert_se(utf8_encoded_to_unichar(i, &c) == r);
|
||||
|
||||
int w = unichar_iswide(c) ? 2 : 1;
|
||||
if (k + w > x)
|
||||
break;
|
||||
@@ -431,9 +460,9 @@ char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
|
||||
continue;
|
||||
}
|
||||
|
||||
tt = utf8_prev_char(t);
|
||||
r = utf8_encoded_to_unichar(tt, &c);
|
||||
if (r < 0)
|
||||
/* Find the previous complete UTF-8 character inside the retained suffix. */
|
||||
tt = find_previous_unichar(i, t, &c);
|
||||
if (!tt)
|
||||
return NULL;
|
||||
|
||||
w = unichar_iswide(c) ? 2 : 1;
|
||||
@@ -451,11 +480,21 @@ char* ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
|
||||
if (k >= new_length) {
|
||||
/* Make space for ellipsis, if required and possible. We know that the edge character is not
|
||||
* part of an ANSI sequence (because then we'd skip it). If the last character we looked at
|
||||
* was wide, we don't need to make space. */
|
||||
if (j < s + old_length)
|
||||
j = utf8_next_char(j);
|
||||
else if (i > s)
|
||||
i = utf8_prev_char(i);
|
||||
* was wide, we don't need to make space.
|
||||
* Move the edge by one complete UTF-8 character within the input slice. */
|
||||
if (j < s + old_length) {
|
||||
r = utf8_encoded_valid_unichar(j, s + old_length - j);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
|
||||
j += r;
|
||||
} else if (i > s) {
|
||||
const char *tt = find_previous_unichar(s, i, NULL);
|
||||
if (!tt)
|
||||
return NULL;
|
||||
|
||||
i = tt;
|
||||
}
|
||||
}
|
||||
|
||||
len = i - s;
|
||||
|
||||
Reference in New Issue
Block a user