Add a config_lines_partition() function to help with LINELIST_V.

This function works a little bit like strsep(), to get a chunk of
configuration lines with a given header.  We can use this to make
hidden service config easier to parse.
This commit is contained in:
Nick Mathewson 2020-02-26 10:10:27 -05:00
parent ba8d71d9c3
commit 9dc946ba67
3 changed files with 81 additions and 0 deletions

View file

@ -253,6 +253,35 @@ config_lines_dup_and_filter(const config_line_t *inp,
return result;
}
/**
* Given a linelist <b>inp</b> beginning with the key <b>header</b>, find the
* next line with that key, and remove that instance and all following lines
* from the list. Return the lines that were removed. Operate
* case-insensitively.
*
* For example, if the header is "H", and <b>inp</b> contains "H, A, B, H, C,
* H, D", this function will alter <b>inp</b> to contain only "H, A, B", and
* return the elements "H, C, H, D" as a separate list.
**/
config_line_t *
config_lines_partition(config_line_t *inp, const char *header)
{
if (BUG(inp == NULL))
return NULL;
if (BUG(strcasecmp(inp->key, header)))
return NULL;
/* Advance ptr until it points to the link to the next segment of this
list. */
config_line_t **ptr = &inp->next;
while (*ptr && strcasecmp((*ptr)->key, header)) {
ptr = &(*ptr)->next;
}
config_line_t *remainder = *ptr;
*ptr = NULL;
return remainder;
}
/** Return true iff a and b contain identical keys and values in identical
* order. */
int

View file

@ -50,6 +50,7 @@ const config_line_t *config_line_find(const config_line_t *lines,
const char *key);
const config_line_t *config_line_find_case(const config_line_t *lines,
const char *key);
config_line_t *config_lines_partition(config_line_t *inp, const char *header);
int config_lines_eq(const config_line_t *a, const config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
void config_free_lines_(config_line_t *front);

View file

@ -1850,6 +1850,56 @@ test_util_config_line_crlf(void *arg)
tor_free(k); tor_free(v);
}
static void
test_util_config_line_partition(void *arg)
{
(void)arg;
config_line_t *lines = NULL, *orig, *rest = NULL;
config_line_append(&lines, "Header", "X");
config_line_append(&lines, "Item", "Y");
config_line_append(&lines, "Thing", "Z");
config_line_append(&lines, "HEADER", "X2");
config_line_append(&lines, "header", "X3");
config_line_append(&lines, "Item3", "Foob");
/* set up h2 and h3 to point to the places where we hope the headers will
be. */
config_line_t *h2 = lines->next->next->next;
config_line_t *h3 = h2->next;
tt_str_op(h2->key, OP_EQ, "HEADER");
tt_str_op(h3->key, OP_EQ, "header");
orig = lines;
rest = config_lines_partition(lines, "Header");
tt_ptr_op(lines, OP_EQ, orig);
tt_ptr_op(rest, OP_EQ, h2);
tt_str_op(lines->next->key, OP_EQ, "Item");
tt_str_op(lines->next->next->key, OP_EQ, "Thing");
tt_ptr_op(lines->next->next->next, OP_EQ, NULL);
config_free_lines(lines);
orig = lines = rest;
rest = config_lines_partition(lines, "Header");
tt_ptr_op(lines, OP_EQ, orig);
tt_ptr_op(rest, OP_EQ, h3);
tt_ptr_op(lines->next, OP_EQ, NULL);
config_free_lines(lines);
orig = lines = rest;
rest = config_lines_partition(lines, "Header");
tt_ptr_op(lines, OP_EQ, orig);
tt_ptr_op(rest, OP_EQ, NULL);
tt_str_op(lines->next->key, OP_EQ, "Item3");
tt_ptr_op(lines->next->next, OP_EQ, NULL);
done:
config_free_lines(lines);
config_free_lines(rest);
}
#ifndef DISABLE_PWDB_TESTS
static void
test_util_expand_filename(void *arg)
@ -6379,6 +6429,7 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(config_line_comment_character),
UTIL_LEGACY(config_line_escaped_content),
UTIL_LEGACY(config_line_crlf),
UTIL_TEST(config_line_partition, 0),
UTIL_TEST_PWDB(expand_filename, 0),
UTIL_LEGACY(escape_string_socks),
UTIL_LEGACY(string_is_key_value),