reftable: check for trailing newline in 'tables.list'
In the reftable format, the 'tables.list' file contains a newline separated list of tables. While we parse this file, we do not check or care about the last newline. Tighten the parser in `parse_names()` to return an appropriate error if the last newline is missing. This requires modification to `parse_names()` to now return the error while accepting the output as a third argument. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Karthik Nayak committed
Oct 7, 2025 at 14:11 UTC
f6442063775b68d9eeaeb9088379fba3298c80ac
4 files changed
+49
-26
reftable/basics.c
+24
-13
@@ -195,44 +195,55 @@ size_t names_length(const char **names)
195
return p - names;
196
}
197
198
-char **parse_names(char *buf, int size)
198
+int parse_names(char *buf, int size, char ***out)
199
{
200
char **names = NULL;
201
size_t names_cap = 0;
202
size_t names_len = 0;
203
char *p = buf;
204
char *end = buf + size;
205
+ int err = 0;
206
207
while (p < end) {
208
char *next = strchr(p, '\n');
208
- if (next && next < end) {
209
- *next = 0;
209
+ if (!next) {
210
+ err = REFTABLE_FORMAT_ERROR;
211
+ goto done;
212
+ } else if (next < end) {
213
+ *next = '\0';
214
} else {
215
next = end;
216
}
217
+
218
if (p < next) {
219
if (REFTABLE_ALLOC_GROW(names, names_len + 1,
215
- names_cap))
216
- goto err;
220
+ names_cap)) {
221
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
222
+ goto done;
223
+ }
224
225
names[names_len] = reftable_strdup(p);
219
- if (!names[names_len++])
220
- goto err;
226
+ if (!names[names_len++]) {
227
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
228
+ goto done;
229
+ }
230
}
231
p = next + 1;
232
}
233
225
- if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap))
226
- goto err;
234
+ if (REFTABLE_ALLOC_GROW(names, names_len + 1, names_cap)) {
235
+ err = REFTABLE_OUT_OF_MEMORY_ERROR;
236
+ goto done;
237
+ }
238
names[names_len] = NULL;
239
229
- return names;
230
-
231
-err:
240
+ *out = names;
241
+ return 0;
242
+done:
243
for (size_t i = 0; i < names_len; i++)
244
reftable_free(names[i]);
245
reftable_free(names);
235
- return NULL;
246
+ return err;
247
}
248
249
int names_equal(const char **a, const char **b)
reftable/basics.h
+4
-3
@@ -167,10 +167,11 @@ void free_names(char **a);
167
168
/*
169
* Parse a newline separated list of names. `size` is the length of the buffer,
170
- * without terminating '\0'. Empty names are discarded. Returns a `NULL`
171
- * pointer when allocations fail.
170
+ * without terminating '\0'. Empty names are discarded.
171
+ *
172
+ * Returns 0 on success, a reftable error code on error.
173
*/
173
-char **parse_names(char *buf, int size);
174
+int parse_names(char *buf, int size, char ***out);
175
176
/* compares two NULL-terminated arrays of strings. */
177
int names_equal(const char **a, const char **b);
reftable/stack.c
+1
-6
@@ -169,12 +169,7 @@ static int fd_read_lines(int fd, char ***namesp)
169
}
170
buf[size] = 0;
171
172
- *namesp = parse_names(buf, size);
173
- if (!*namesp) {
174
- err = REFTABLE_OUT_OF_MEMORY_ERROR;
175
- goto done;
176
- }
177
-
172
+ err = parse_names(buf, size, namesp);
173
done:
174
reftable_free(buf);
175
return err;
t/unit-tests/u-reftable-basics.c
+20
-4
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
9
#include "unit-test.h"
10
#include "lib-reftable.h"
11
#include "reftable/basics.h"
12
+#include "reftable/reftable-error.h"
13
14
struct integer_needle_lesseq_args {
15
int needle;
@@ -79,14 +80,18 @@ void test_reftable_basics__names_equal(void)
80
void test_reftable_basics__parse_names(void)
81
{
82
char in1[] = "line\n";
82
- char in2[] = "a\nb\nc";
83
- char **out = parse_names(in1, strlen(in1));
83
+ char in2[] = "a\nb\nc\n";
84
+ char **out = NULL;
85
+ int err = parse_names(in1, strlen(in1), &out);
86
+ cl_assert(err == 0);
87
cl_assert(out != NULL);
88
cl_assert_equal_s(out[0], "line");
89
cl_assert(!out[1]);
90
free_names(out);
91
89
- out = parse_names(in2, strlen(in2));
92
+ out = NULL;
93
+ err = parse_names(in2, strlen(in2), &out);
94
+ cl_assert(err == 0);
95
cl_assert(out != NULL);
96
cl_assert_equal_s(out[0], "a");
97
cl_assert_equal_s(out[1], "b");
@@ -95,10 +100,21 @@ void test_reftable_basics__parse_names(void)
100
free_names(out);
101
}
102
103
+void test_reftable_basics__parse_names_missing_newline(void)
104
+{
105
+ char in1[] = "line\nline2";
106
+ char **out = NULL;
107
+ int err = parse_names(in1, strlen(in1), &out);
108
+ cl_assert(err == REFTABLE_FORMAT_ERROR);
109
+ cl_assert(out == NULL);
110
+}
111
+
112
void test_reftable_basics__parse_names_drop_empty_string(void)
113
{
114
char in[] = "a\n\nb\n";
101
- char **out = parse_names(in, strlen(in));
115
+ char **out = NULL;
116
+ int err = parse_names(in, strlen(in), &out);
117
+ cl_assert(err == 0);
118
cl_assert(out != NULL);
119
cl_assert_equal_s(out[0], "a");
120
/* simply '\n' should be dropped as empty string */