diff --git a/src/config/conf.c b/src/config/conf.c index aad84af7b..a3c87f857 100644 --- a/src/config/conf.c +++ b/src/config/conf.c @@ -292,7 +292,9 @@ parse_set(struct option *opt_tree, struct conf_parsing_state *state, } } else { /* rewriting a configuration file */ - if (opt->flags & OPT_DELETED) { + struct option *flagsite = indirect_option(opt); + + if (flagsite->flags & OPT_DELETED) { /* Replace the "set" command with an * "unset" command. */ add_to_string(mirror, "unset "); @@ -308,7 +310,7 @@ parse_set(struct option *opt_tree, struct conf_parsing_state *state, } /* Remember that the option need not be * written to the end of the file. */ - opt->flags &= ~OPT_MUST_SAVE; + flagsite->flags &= ~OPT_MUST_SAVE; } mem_free(val); } @@ -360,7 +362,9 @@ parse_unset(struct option *opt_tree, struct conf_parsing_state *state, else mark_option_as_deleted(opt); } else { /* rewriting a configuration file */ - if (opt->flags & OPT_DELETED) { + struct option *flagsite = indirect_option(opt); + + if (flagsite->flags & OPT_DELETED) { /* The "unset" command is already in the file, * and unlike with "set", there is no value * to be updated. */ @@ -376,7 +380,7 @@ parse_unset(struct option *opt_tree, struct conf_parsing_state *state, } /* Remember that the option need not be * written to the end of the file. */ - opt->flags &= ~OPT_MUST_SAVE; + flagsite->flags &= ~OPT_MUST_SAVE; } } diff --git a/src/config/options.c b/src/config/options.c index 6198051f4..14ab8db9a 100644 --- a/src/config/options.c +++ b/src/config/options.c @@ -164,7 +164,7 @@ static int no_autocreate = 0; * work this way because the alias may have the ::OPT_ALIAS_NEGATE flag. * Instead, if the caller tries to read or set the value of the alias, * the functions associated with ::OPT_ALIAS will forward the operation - * to the underlying option. */ + * to the underlying option. However, see indirect_option(). */ struct option * get_opt_rec(struct option *tree, const unsigned char *name_) { @@ -248,6 +248,27 @@ get_opt_rec_real(struct option *tree, const unsigned char *name) return opt; } +/** If @a opt is an alias, return the option to which it refers. + * + * @warning Because the alias may have the ::OPT_ALIAS_NEGATE flag, + * the caller must not access the value of the returned option as if + * it were also the value of the alias. However, it is safe to access + * flags such as ::OPT_MUST_SAVE and ::OPT_DELETED. */ +struct option * +indirect_option(struct option *alias) +{ + struct option *real; + + if (alias->type != OPT_ALIAS) return alias; /* not an error */ + + real = get_opt_rec(config_options, alias->value.string); + assertm(real != NULL, "%s aliased to unknown option %s!", + alias->name, alias->value.string); + if_assert_failed return alias; + + return real; +} + /* Fetch pointer to value of certain option. It is guaranteed to never return * NULL. Note that you are supposed to use wrapper get_opt(). */ union option_value * diff --git a/src/config/options.h b/src/config/options.h index 2d6f506a9..3d9b45ba8 100644 --- a/src/config/options.h +++ b/src/config/options.h @@ -223,6 +223,7 @@ extern void checkout_option_values(struct option_resolver *resolvers, extern struct option *get_opt_rec(struct option *, const unsigned char *); extern struct option *get_opt_rec_real(struct option *, const unsigned char *); +struct option *indirect_option(struct option *); #ifdef CONFIG_DEBUG extern union option_value *get_opt_(unsigned char *, int, enum option_type, struct option *, unsigned char *); #define get_opt(tree, name, type) get_opt_(__FILE__, __LINE__, type, tree, name)