core-lightning/common/configvar.c
Rusty Russell 62f531a1f2 lightningd: configvar style fixes
1) We can't simply cast away const to manipulate a string, the compiler can assume
   we don't.  The type must be made non-const.
2) cisspace() is nicer to use than isspace() (no cast required!)
3) Simply place a NUL terminator instead of using memmove to set it.
4) Use cast_const to add const to char **, where necessary.
5) Add Changelog line, for CHANGELOG.md

Changelog-Fixed: Config: whitespace at the end of (most) options is now ignored, not complained about.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2024-08-10 15:20:59 +09:30

241 lines
5.9 KiB
C

#include "config.h"
#include <assert.h>
#include <ccan/cast/cast.h>
#include <ccan/tal/str/str.h>
#include <common/configvar.h>
#include <common/utils.h>
#include <unistd.h>
struct configvar *configvar_new(const tal_t *ctx,
enum configvar_src src,
const char *file,
size_t linenum,
const char *configline)
{
struct configvar *cv = tal(ctx, struct configvar);
cv->file = tal_strdup_or_null(cv, file);
cv->src = src;
cv->linenum = linenum;
cv->configline = tal_strdup(cv, configline);
cv->overridden = false;
cv->optvar = NULL;
/* We fill in cv->optvar and cv->optarg when parsing! */
return cv;
}
const struct opt_table *configvar_unparsed(struct configvar *cv)
{
const struct opt_table *ot;
if (cv->src == CONFIGVAR_CMDLINE_SHORT) {
ot = opt_find_short(cv->configline[0]);
cv->optarg = NULL;
} else {
ot = opt_find_long(cv->configline, cast_const2(const char **, &cv->optarg));
}
if (!ot)
return NULL;
/* We get called multiple times, but we're expected to always
* finish the cv vars, even if they're added at the last minute
* on the cmdline, so we check this is only done once, and we
* do it even if we're not going to use it now. */
if (!cv->optvar) {
/* optvar is up to the = (i.e. one char before optarg) */
if (!cv->optarg)
cv->optvar = cv->configline;
else
cv->optvar = tal_strndup(cv, cv->configline,
cv->optarg - cv->configline - 1);
}
return ot;
}
static void trim_whitespace(char *s)
{
size_t len = strlen(s);
while (len > 0 && cisspace(s[len - 1]))
len--;
s[len] = '\0';
}
const char *configvar_parse(struct configvar *cv,
bool early,
bool full_knowledge,
bool developer)
{
const struct opt_table *ot;
ot = configvar_unparsed(cv);
if (!ot) {
/* Do we ignore unknown entries? */
if (!full_knowledge)
return NULL;
return "unknown option";
}
if ((ot->type & OPT_DEV) && !developer)
return "requires --developer";
/* If we're early and we want late, or vv, ignore. */
if (!!(ot->type & OPT_EARLY) != early)
return NULL;
if (ot->type & OPT_NOARG) {
/* MULTI doesn't make sense with single args */
assert(!(ot->type & OPT_MULTI));
if (cv->optarg)
return "doesn't allow an argument";
return ot->cb(ot->u.arg);
} else {
if (!cv->optarg)
return "requires an argument";
if (!(ot->type & OPT_KEEP_WHITESPACE))
trim_whitespace(cv->optarg);
return ot->cb_arg(cv->optarg, ot->u.arg);
}
}
/* This is O(N^2) but nobody cares */
void configvar_finalize_overrides(struct configvar **cvs)
{
/* Map to options: two different names can be the same option,
* given aliases! */
const struct opt_table **opts;
opts = tal_arr(tmpctx, const struct opt_table *, tal_count(cvs));
for (size_t i = 0; i < tal_count(cvs); i++) {
opts[i] = opt_find_long(cvs[i]->optvar, NULL);
/* If you're allowed multiple, they don't override */
if (opts[i]->type & OPT_MULTI)
continue;
for (size_t j = 0; j < i; j++) {
if (opts[j] == opts[i])
cvs[j]->overridden = true;
}
}
}
void configvar_remove(struct configvar ***cvs,
const char *name,
enum configvar_src src,
const char *optarg)
{
/* We remove all from this source, potentially restoring an overridden */
ssize_t prev = -1;
bool removed;
removed = false;
for (size_t i = 0; i < tal_count(*cvs); i++) {
/* This can happen if plugin fails during startup! */
if ((*cvs)[i]->optvar == NULL)
continue;
if (!streq((*cvs)[i]->optvar, name))
continue;
if (optarg && !streq((*cvs)[i]->optarg, optarg))
continue;
if ((*cvs)[i]->src == src) {
tal_free((*cvs)[i]);
tal_arr_remove(cvs, i);
i--;
removed = true;
continue;
}
/* Wrong type, correct name. */
prev = i;
}
/* Unmark prev if we removed overriding ones. If it's multi,
* this is a noop. */
if (removed && prev != -1)
(*cvs)[prev]->overridden = false;
}
struct configvar *configvar_dup(const tal_t *ctx, const struct configvar *cv)
{
struct configvar *ret;
if (taken(cv))
return tal_steal(ctx, cast_const(struct configvar *, cv));
ret = tal_dup(ctx, struct configvar, cv);
if (ret->file)
ret->file = tal_strdup(ret, ret->file);
if (ret->configline)
ret->configline = tal_strdup(ret, ret->configline);
if (ret->optvar) {
ret->optvar = tal_strdup(ret, ret->optvar);
/* Optarg, if non-NULL, points into cmdline! */
if (ret->optarg) {
size_t off = cv->optarg - cv->configline;
assert(off < strlen(cv->configline));
ret->optarg = ret->configline + off;
}
}
return ret;
}
struct configvar **configvar_join(const tal_t *ctx,
struct configvar **first,
struct configvar **second)
{
struct configvar **cvs;
size_t n = tal_count(first);
if (taken(first)) {
cvs = tal_steal(ctx, first);
tal_resize(&cvs, n + tal_count(second));
} else {
cvs = tal_arr(ctx, struct configvar *, n + tal_count(second));
for (size_t i = 0; i < n; i++) {
cvs[i] = configvar_dup(cvs, first[i]);
}
}
if (taken(second)) {
for (size_t i = 0; i < tal_count(second); i++)
cvs[n + i] = tal_steal(cvs, second[i]);
tal_free(second);
} else {
for (size_t i = 0; i < tal_count(second); i++)
cvs[n + i] = configvar_dup(cvs, second[i]);
}
return cvs;
}
static struct configvar *configvar_iter(struct configvar **cvs,
const char **names,
const struct configvar *firstcv)
{
for (size_t i = 0; i < tal_count(cvs); i++) {
/* Wait until we reach firstcv, if any */
if (firstcv) {
if (cvs[i] == firstcv)
firstcv = NULL;
continue;
}
for (size_t j = 0; j < tal_count(names); j++) {
/* In case we iterate before initialization! */
if (!cvs[i]->optvar)
continue;
if (streq(cvs[i]->optvar, names[j]) && !cvs[i]->overridden)
return cvs[i];
}
}
return NULL;
}
struct configvar *configvar_first(struct configvar **cvs, const char **names)
{
return configvar_iter(cvs, names, NULL);
}
struct configvar *configvar_next(struct configvar **cvs,
const struct configvar *cv,
const char **names)
{
return configvar_iter(cvs, names, cv);
}