mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 04:13:28 +01:00
kvline: handle empty alues as well as empty keys
The two options are mutually exclusive, since otherwise an entry like "Foo" would be ambiguous. We want to have the ability to treat entries like this as keys, though, since some controller commands interpret them as flags.
This commit is contained in:
parent
01b07c548b
commit
73df91bbb5
@ -53,6 +53,15 @@ line_has_no_key(const config_line_t *line)
|
||||
return line->key == NULL || strlen(line->key) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff the value in <b>line</b> is not set.
|
||||
**/
|
||||
static bool
|
||||
line_has_no_val(const config_line_t *line)
|
||||
{
|
||||
return line->value == NULL || strlen(line->value) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff the all the lines in <b>line</b> can be encoded
|
||||
* using <b>flags</b>.
|
||||
@ -98,6 +107,10 @@ kvline_can_encode_lines(const config_line_t *line, unsigned flags)
|
||||
* If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
|
||||
* allowed, and are encoded as 'Value'. Otherwise, such pairs are not
|
||||
* allowed.
|
||||
*
|
||||
* If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
|
||||
* encoded as 'Key', not as 'Key=' or 'Key=""'. Mutually exclusive with
|
||||
* KV_OMIT_KEYS.
|
||||
*/
|
||||
char *
|
||||
kvline_encode(const config_line_t *line,
|
||||
@ -106,6 +119,9 @@ kvline_encode(const config_line_t *line,
|
||||
if (!kvline_can_encode_lines(line, flags))
|
||||
return NULL;
|
||||
|
||||
tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
|
||||
(KV_OMIT_KEYS|KV_OMIT_VALS));
|
||||
|
||||
smartlist_t *elements = smartlist_new();
|
||||
|
||||
for (; line; line = line->next) {
|
||||
@ -126,7 +142,10 @@ kvline_encode(const config_line_t *line,
|
||||
}
|
||||
}
|
||||
|
||||
if (esc) {
|
||||
if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
|
||||
eq = "";
|
||||
v = "";
|
||||
} else if (esc) {
|
||||
tmp = esc_for_log(line->value);
|
||||
v = tmp;
|
||||
} else {
|
||||
@ -155,13 +174,21 @@ kvline_encode(const config_line_t *line,
|
||||
*
|
||||
* If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
|
||||
* allowed. Otherwise, such values are not allowed.
|
||||
*
|
||||
* If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
|
||||
* allowed. Otherwise, such keys are not allowed. Mutually exclusive with
|
||||
* KV_OMIT_KEYS.
|
||||
*/
|
||||
config_line_t *
|
||||
kvline_parse(const char *line, unsigned flags)
|
||||
{
|
||||
tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
|
||||
(KV_OMIT_KEYS|KV_OMIT_VALS));
|
||||
|
||||
const char *cp = line, *cplast = NULL;
|
||||
bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
|
||||
bool quoted = (flags & KV_QUOTED) != 0;
|
||||
const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
|
||||
const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
|
||||
const bool quoted = (flags & KV_QUOTED) != 0;
|
||||
|
||||
config_line_t *result = NULL;
|
||||
config_line_t **next_line = &result;
|
||||
@ -171,27 +198,33 @@ kvline_parse(const char *line, unsigned flags)
|
||||
|
||||
while (*cp) {
|
||||
key = val = NULL;
|
||||
/* skip all spaces */
|
||||
{
|
||||
size_t idx = strspn(cp, " \t\r\v\n");
|
||||
cp += idx;
|
||||
}
|
||||
if (BUG(cp == cplast)) {
|
||||
/* If we didn't parse anything, this code is broken. */
|
||||
/* If we didn't parse anything since the last loop, this code is
|
||||
* broken. */
|
||||
goto err; // LCOV_EXCL_LINE
|
||||
}
|
||||
cplast = cp;
|
||||
if (! *cp)
|
||||
break; /* End of string; we're done. */
|
||||
|
||||
/* Possible formats are K=V, K="V", V, and "V", depending on flags. */
|
||||
/* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
|
||||
|
||||
/* Find the key. */
|
||||
/* Find where the key ends */
|
||||
if (*cp != '\"') {
|
||||
size_t idx = strcspn(cp, " \t\r\v\n=");
|
||||
|
||||
if (cp[idx] == '=') {
|
||||
key = tor_memdup_nulterm(cp, idx);
|
||||
cp += idx + 1;
|
||||
} else if (omit_vals) {
|
||||
key = tor_memdup_nulterm(cp, idx);
|
||||
cp += idx;
|
||||
goto commit;
|
||||
} else {
|
||||
if (!omit_keys)
|
||||
goto err;
|
||||
@ -214,6 +247,7 @@ kvline_parse(const char *line, unsigned flags)
|
||||
cp += idx;
|
||||
}
|
||||
|
||||
commit:
|
||||
if (key && strlen(key) == 0) {
|
||||
/* We don't allow empty keys. */
|
||||
goto err;
|
||||
@ -221,7 +255,7 @@ kvline_parse(const char *line, unsigned flags)
|
||||
|
||||
*next_line = tor_malloc_zero(sizeof(config_line_t));
|
||||
(*next_line)->key = key ? key : tor_strdup("");
|
||||
(*next_line)->value = val;
|
||||
(*next_line)->value = val ? val : tor_strdup("");
|
||||
next_line = &(*next_line)->next;
|
||||
key = val = NULL;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ struct config_line_t;
|
||||
|
||||
#define KV_QUOTED (1u<<0)
|
||||
#define KV_OMIT_KEYS (1u<<1)
|
||||
#define KV_OMIT_VALS (1u<<2)
|
||||
|
||||
struct config_line_t *kvline_parse(const char *line, unsigned flags);
|
||||
char *kvline_encode(const struct config_line_t *line, unsigned flags);
|
||||
|
@ -5886,6 +5886,61 @@ test_config_kvline_parse(void *arg)
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "");
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse("AB=", KV_OMIT_VALS);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "");
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse(" AB ", KV_OMIT_VALS);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "");
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse("AB", KV_OMIT_VALS);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "");
|
||||
enc = kvline_encode(lines, KV_OMIT_VALS);
|
||||
tt_str_op(enc, OP_EQ, "AB");
|
||||
tor_free(enc);
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse("AB=CD", KV_OMIT_VALS);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "CD");
|
||||
enc = kvline_encode(lines, KV_OMIT_VALS);
|
||||
tt_str_op(enc, OP_EQ, "AB=CD");
|
||||
tor_free(enc);
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse("AB=CD DE FGH=I", KV_OMIT_VALS);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "CD");
|
||||
tt_str_op(lines->next->key, OP_EQ, "DE");
|
||||
tt_str_op(lines->next->value, OP_EQ, "");
|
||||
tt_str_op(lines->next->next->key, OP_EQ, "FGH");
|
||||
tt_str_op(lines->next->next->value, OP_EQ, "I");
|
||||
enc = kvline_encode(lines, KV_OMIT_VALS);
|
||||
tt_str_op(enc, OP_EQ, "AB=CD DE FGH=I");
|
||||
tor_free(enc);
|
||||
config_free_lines(lines);
|
||||
|
||||
lines = kvline_parse("AB=\"CD E\" DE FGH=\"I\"", KV_OMIT_VALS|KV_QUOTED);
|
||||
tt_assert(lines);
|
||||
tt_str_op(lines->key, OP_EQ, "AB");
|
||||
tt_str_op(lines->value, OP_EQ, "CD E");
|
||||
tt_str_op(lines->next->key, OP_EQ, "DE");
|
||||
tt_str_op(lines->next->value, OP_EQ, "");
|
||||
tt_str_op(lines->next->next->key, OP_EQ, "FGH");
|
||||
tt_str_op(lines->next->next->value, OP_EQ, "I");
|
||||
enc = kvline_encode(lines, KV_OMIT_VALS|KV_QUOTED);
|
||||
tt_str_op(enc, OP_EQ, "AB=\"CD E\" DE FGH=I");
|
||||
|
||||
done:
|
||||
config_free_lines(lines);
|
||||
|
Loading…
Reference in New Issue
Block a user