reftable/basics: handle allocation failures in parse_names()

Handle allocation failures in `parse_names()` by returning `NULL` in
case any allocation fails. While at it, refactor the function to return
the array directly instead of assigning it to an out-pointer.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Patrick Steinhardt
2024-10-02 12:55:38 +02:00
committed by Junio C Hamano
parent 6593e147d3
commit eef7bcdafe
4 changed files with 33 additions and 13 deletions

View File

@ -135,14 +135,14 @@ size_t names_length(const char **names)
return p - names; return p - names;
} }
void parse_names(char *buf, int size, char ***namesp) char **parse_names(char *buf, int size)
{ {
char **names = NULL; char **names = NULL;
size_t names_cap = 0; size_t names_cap = 0;
size_t names_len = 0; size_t names_len = 0;
char *p = buf; char *p = buf;
char *end = buf + size; char *end = buf + size;
while (p < end) { while (p < end) {
char *next = strchr(p, '\n'); char *next = strchr(p, '\n');
if (next && next < end) { if (next && next < end) {
@ -152,14 +152,26 @@ void parse_names(char *buf, int size, char ***namesp)
} }
if (p < next) { if (p < next) {
REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap); REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap);
names[names_len++] = xstrdup(p); if (!names)
goto err;
names[names_len] = reftable_strdup(p);
if (!names[names_len++])
goto err;
} }
p = next + 1; p = next + 1;
} }
REFTABLE_REALLOC_ARRAY(names, names_len + 1); REFTABLE_REALLOC_ARRAY(names, names_len + 1);
names[names_len] = NULL; names[names_len] = NULL;
*namesp = names;
return names;
err:
for (size_t i = 0; i < names_len; i++)
reftable_free(names[i]);
reftable_free(names);
return NULL;
} }
int names_equal(const char **a, const char **b) int names_equal(const char **a, const char **b)

View File

@ -38,9 +38,12 @@ size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
*/ */
void free_names(char **a); void free_names(char **a);
/* parse a newline separated list of names. `size` is the length of the buffer, /*
* without terminating '\0'. Empty names are discarded. */ * Parse a newline separated list of names. `size` is the length of the buffer,
void parse_names(char *buf, int size, char ***namesp); * without terminating '\0'. Empty names are discarded. Returns a `NULL`
* pointer when allocations fail.
*/
char **parse_names(char *buf, int size);
/* compares two NULL-terminated arrays of strings. */ /* compares two NULL-terminated arrays of strings. */
int names_equal(const char **a, const char **b); int names_equal(const char **a, const char **b);

View File

@ -108,7 +108,11 @@ static int fd_read_lines(int fd, char ***namesp)
} }
buf[size] = 0; buf[size] = 0;
parse_names(buf, size, namesp); *namesp = parse_names(buf, size);
if (!*namesp) {
err = REFTABLE_OUT_OF_MEMORY_ERROR;
goto done;
}
done: done:
reftable_free(buf); reftable_free(buf);

View File

@ -72,13 +72,14 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
if_test ("parse_names works for basic input") { if_test ("parse_names works for basic input") {
char in1[] = "line\n"; char in1[] = "line\n";
char in2[] = "a\nb\nc"; char in2[] = "a\nb\nc";
char **out = NULL; char **out = parse_names(in1, strlen(in1));
parse_names(in1, strlen(in1), &out); check(out != NULL);
check_str(out[0], "line"); check_str(out[0], "line");
check(!out[1]); check(!out[1]);
free_names(out); free_names(out);
parse_names(in2, strlen(in2), &out); out = parse_names(in2, strlen(in2));
check(out != NULL);
check_str(out[0], "a"); check_str(out[0], "a");
check_str(out[1], "b"); check_str(out[1], "b");
check_str(out[2], "c"); check_str(out[2], "c");
@ -88,8 +89,8 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
if_test ("parse_names drops empty string") { if_test ("parse_names drops empty string") {
char in[] = "a\n\nb\n"; char in[] = "a\n\nb\n";
char **out = NULL; char **out = parse_names(in, strlen(in));
parse_names(in, strlen(in), &out); check(out != NULL);
check_str(out[0], "a"); check_str(out[0], "a");
/* simply '\n' should be dropped as empty string */ /* simply '\n' should be dropped as empty string */
check_str(out[1], "b"); check_str(out[1], "b");