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>

Patrick Steinhardt committed Oct 2, 2024 at 12:55 UTC eef7bcdafe0037f14a96c564ace899342b9ed0fb
4 files changed +33 -13
reftable/basics.c
+16 -4
@@ -135,14 +135,14 @@ size_t names_length(const char **names)
135 return p - names;
136 }
137
138 -void parse_names(char *buf, int size, char ***namesp)
138 +char **parse_names(char *buf, int size)
139 {
140 char **names = NULL;
141 size_t names_cap = 0;
142 size_t names_len = 0;
143 -
143 char *p = buf;
144 char *end = buf + size;
145 +
146 while (p < end) {
147 char *next = strchr(p, '\n');
148 if (next && next < end) {
@@ -152,14 +152,26 @@ void parse_names(char *buf, int size, char ***namesp)
152 }
153 if (p < next) {
154 REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap);
155 - names[names_len++] = xstrdup(p);
155 + if (!names)
156 + goto err;
157 +
158 + names[names_len] = reftable_strdup(p);
159 + if (!names[names_len++])
160 + goto err;
161 }
162 p = next + 1;
163 }
164
165 REFTABLE_REALLOC_ARRAY(names, names_len + 1);
166 names[names_len] = NULL;
162 - *namesp = names;
167 +
168 + return names;
169 +
170 +err:
171 + for (size_t i = 0; i < names_len; i++)
172 + reftable_free(names[i]);
173 + reftable_free(names);
174 + return NULL;
175 }
176
177 int names_equal(const char **a, const char **b)
reftable/basics.h
+6 -3
@@ -38,9 +38,12 @@ size_t binsearch(size_t sz, int (*f)(size_t k, void *args), void *args);
38 */
39 void free_names(char **a);
40
41 -/* parse a newline separated list of names. `size` is the length of the buffer,
42 - * without terminating '\0'. Empty names are discarded. */
43 -void parse_names(char *buf, int size, char ***namesp);
41 +/*
42 + * Parse a newline separated list of names. `size` is the length of the buffer,
43 + * without terminating '\0'. Empty names are discarded. Returns a `NULL`
44 + * pointer when allocations fail.
45 + */
46 +char **parse_names(char *buf, int size);
47
48 /* compares two NULL-terminated arrays of strings. */
49 int names_equal(const char **a, const char **b);
reftable/stack.c
+5 -1
@@ -108,7 +108,11 @@ static int fd_read_lines(int fd, char ***namesp)
108 }
109 buf[size] = 0;
110
111 - parse_names(buf, size, namesp);
111 + *namesp = parse_names(buf, size);
112 + if (!*namesp) {
113 + err = REFTABLE_OUT_OF_MEMORY_ERROR;
114 + goto done;
115 + }
116
117 done:
118 reftable_free(buf);
t/unit-tests/t-reftable-basics.c
+6 -5
@@ -72,13 +72,14 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
72 if_test ("parse_names works for basic input") {
73 char in1[] = "line\n";
74 char in2[] = "a\nb\nc";
75 - char **out = NULL;
76 - parse_names(in1, strlen(in1), &out);
75 + char **out = parse_names(in1, strlen(in1));
76 + check(out != NULL);
77 check_str(out[0], "line");
78 check(!out[1]);
79 free_names(out);
80
81 - parse_names(in2, strlen(in2), &out);
81 + out = parse_names(in2, strlen(in2));
82 + check(out != NULL);
83 check_str(out[0], "a");
84 check_str(out[1], "b");
85 check_str(out[2], "c");
@@ -88,8 +89,8 @@ int cmd_main(int argc UNUSED, const char *argv[] UNUSED)
89
90 if_test ("parse_names drops empty string") {
91 char in[] = "a\n\nb\n";
91 - char **out = NULL;
92 - parse_names(in, strlen(in), &out);
92 + char **out = parse_names(in, strlen(in));
93 + check(out != NULL);
94 check_str(out[0], "a");
95 /* simply '\n' should be dropped as empty string */
96 check_str(out[1], "b");