mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-21 17:47:10 +00:00
parser: refactor variables and symbols table into their own class
While symtab for now has only static members, it will allow for a change in the future for each profile to have their own symbols like profile_name, etc.
This commit is contained in:
parent
0ea74cd19d
commit
dfbd2dc4b1
@ -105,12 +105,12 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
||||
parser_alias.c common_optarg.c lib.c network.cc \
|
||||
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
|
||||
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
|
||||
mqueue.cc io_uring.cc all_rule.cc cond_expr.cc
|
||||
mqueue.cc io_uring.cc all_rule.cc cond_expr.cc variable.cc symtab.cc
|
||||
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
|
||||
file_cache.h immunix.h lib.h mount.h network.h parser.h \
|
||||
parser_include.h parser_version.h policy_cache.h policydb.h \
|
||||
profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
|
||||
common_flags.h bignum.h all_rule.h cond_expr.h
|
||||
common_flags.h bignum.h all_rule.h cond_expr.h variable.h symtab.h
|
||||
|
||||
SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
|
||||
GENERATED_HDRS = af_names.h generated_af_names.h \
|
||||
@ -331,6 +331,13 @@ all_rule.o: all_rule.cc $(HDRS)
|
||||
cond_expr.o: cond_expr.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
variable.o: variable.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
symtab.o: symtab.cc $(HDRS)
|
||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
|
||||
parser_version.h: Makefile
|
||||
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
||||
@mv -f .ver $@
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "cond_expr.h"
|
||||
#include "parser.h"
|
||||
#include "symtab.h"
|
||||
|
||||
cond_expr::cond_expr(bool result):
|
||||
result(result)
|
||||
@ -26,20 +27,21 @@ cond_expr::cond_expr(bool result):
|
||||
|
||||
cond_expr::cond_expr(const char *var, bool defined)
|
||||
{
|
||||
char *var_name = process_var(var);
|
||||
|
||||
variable *ref;
|
||||
if (!defined) {
|
||||
int ret = get_boolean_var(var_name);
|
||||
if (ret < 0) {
|
||||
ref = symtab::get_boolean_var(var);
|
||||
if (!ref) {
|
||||
/* FIXME check for set var */
|
||||
free(var_name);
|
||||
yyerror(_("Unset boolean variable %s used in if-expression"), var);
|
||||
}
|
||||
result = ret;
|
||||
result = ref->boolean;
|
||||
} else {
|
||||
void *set_value = get_set_var(var_name);
|
||||
PDEBUG("Matched: defined set expr %s value %lx\n", var_name, (long) set_value);
|
||||
result = !! (long) set_value;
|
||||
ref = symtab::get_set_var(var);
|
||||
if (!ref) {
|
||||
result = false;
|
||||
} else {
|
||||
PDEBUG("Matched: defined set expr %s value %s\n", var, ref->expanded.begin()->c_str());
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
free(var_name);
|
||||
}
|
||||
|
@ -415,8 +415,6 @@ extern const char *local_name(const char *name);
|
||||
/* parser_variable.c */
|
||||
int expand_entry_variables(char **name);
|
||||
extern int process_variables(Profile *prof);
|
||||
extern struct var_string *split_out_var(const char *string);
|
||||
extern void free_var_string(struct var_string *var);
|
||||
|
||||
/* parser_misc.c */
|
||||
extern void warn_uppercase(void);
|
||||
@ -458,38 +456,6 @@ bool entry_add_prefix(struct cod_entry *entry, const prefixes &p, const char *&e
|
||||
#define SECONDS_P_MS (1000LL * 1000LL)
|
||||
long long convert_time_units(long long value, long long base, const char *units);
|
||||
|
||||
|
||||
/* parser_symtab.c */
|
||||
struct set_value {
|
||||
char *val;
|
||||
struct set_value *next;
|
||||
};
|
||||
enum var_type {
|
||||
sd_boolean,
|
||||
sd_set,
|
||||
};
|
||||
|
||||
struct symtab {
|
||||
char *var_name;
|
||||
enum var_type type;
|
||||
int boolean;
|
||||
struct set_value *values;
|
||||
struct set_value *expanded;
|
||||
};
|
||||
|
||||
extern int add_boolean_var(const char *var, int boolean);
|
||||
extern int get_boolean_var(const char *var);
|
||||
extern int new_set_var(const char *var, const char *value);
|
||||
extern int add_set_value(const char *var, const char *value);
|
||||
extern struct set_value *get_set_var(const char *var);
|
||||
extern char *get_next_set_value(struct set_value **context);
|
||||
extern int insert_set_var(struct symtab *var);
|
||||
extern struct symtab *remove_set_var(const char *var_name);
|
||||
extern int delete_set_var(const char *var_name);
|
||||
extern void dump_symtab(void);
|
||||
extern void dump_expanded_symtab(void);
|
||||
void free_symtabs(void);
|
||||
|
||||
/* parser_alias.c */
|
||||
extern int new_alias(const char *from, const char *to);
|
||||
extern int replace_profile_aliases(Profile *prof);
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "policy_cache.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "file_cache.h"
|
||||
#include "symtab.h"
|
||||
|
||||
#define OLD_MODULE_NAME "subdomain"
|
||||
#define PROC_MODULES "/proc/modules"
|
||||
@ -1092,7 +1093,7 @@ void reset_parser(const char *filename)
|
||||
memset(&cache_tstamp, 0, sizeof(cache_tstamp));
|
||||
mru_skip_cache = 1;
|
||||
free_aliases();
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
free_policies();
|
||||
reset_include_stack(filename);
|
||||
aa_features_unref(policy_features);
|
||||
@ -1223,7 +1224,7 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
}
|
||||
|
||||
if (dump_vars) {
|
||||
dump_symtab();
|
||||
symtab::dump(false);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1234,7 +1235,7 @@ int process_profile(int option, aa_kernel_interface *kernel_interface,
|
||||
}
|
||||
|
||||
if (dump_expanded_vars) {
|
||||
dump_expanded_symtab();
|
||||
symtab::dump(true);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -532,35 +532,6 @@ char *processid(const char *string, int len)
|
||||
return processunquoted(string, len);
|
||||
}
|
||||
|
||||
/* strip off surrounding delimiters around variables */
|
||||
char *process_var(const char *var)
|
||||
{
|
||||
const char *orig = var;
|
||||
int len = strlen(var);
|
||||
|
||||
if (*orig == '@' || *orig == '$') {
|
||||
orig++;
|
||||
len--;
|
||||
} else {
|
||||
PERROR("ASSERT: Found var '%s' without variable prefix\n",
|
||||
var);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*orig == '{') {
|
||||
orig++;
|
||||
len--;
|
||||
if (orig[len - 1] != '}') {
|
||||
PERROR("ASSERT: No matching '}' in variable '%s'\n",
|
||||
var);
|
||||
return NULL;
|
||||
} else
|
||||
len--;
|
||||
}
|
||||
|
||||
return strndup(orig, len);
|
||||
}
|
||||
|
||||
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
|
||||
int str_to_boolean(const char *value)
|
||||
{
|
||||
|
@ -24,654 +24,27 @@
|
||||
|
||||
#include "immunix.h"
|
||||
#include "parser.h"
|
||||
|
||||
typedef int (*comparison_fn_t)(const void *, const void *);
|
||||
typedef void (*__free_fn_t)(void *);
|
||||
|
||||
|
||||
static void *my_symtab = NULL;
|
||||
|
||||
static int __expand_variable(struct symtab *symbol);
|
||||
|
||||
static struct symtab *new_symtab_entry(const char *name)
|
||||
{
|
||||
struct symtab *n = (struct symtab *) calloc(1, sizeof(*n));
|
||||
|
||||
if (!n) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n->var_name = strndup(name, PATH_MAX);
|
||||
if (!n->var_name) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
free(n);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct set_value *new_set_value(const char *val)
|
||||
{
|
||||
struct set_value *n = (struct set_value *) calloc(1, sizeof(*n));
|
||||
|
||||
if (!n) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n->val = strndup(val, PATH_MAX);
|
||||
if (!n->val) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
free(n);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void free_values(struct set_value *val)
|
||||
{
|
||||
struct set_value *i = val, *tmp;
|
||||
|
||||
while (i) {
|
||||
if (i->val)
|
||||
free(i->val);
|
||||
tmp = i;
|
||||
i = i->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_symtab(struct symtab *symtab)
|
||||
{
|
||||
if (!symtab)
|
||||
return;
|
||||
|
||||
if (symtab->var_name)
|
||||
free(symtab->var_name);
|
||||
|
||||
free_values(symtab->values);
|
||||
free_values(symtab->expanded);
|
||||
free(symtab);
|
||||
}
|
||||
|
||||
/* abstract this out in case we switch data structures */
|
||||
static void add_to_set(struct set_value **list, const char *val)
|
||||
{
|
||||
struct set_value *new_item = new_set_value(val);
|
||||
|
||||
new_item->next = *list;
|
||||
*list = new_item;
|
||||
}
|
||||
|
||||
static int compare_symtabs(const void *a, const void *b)
|
||||
{
|
||||
char *a_name = ((struct symtab *) a)->var_name;
|
||||
char *b_name = ((struct symtab *) b)->var_name;
|
||||
return strcmp(a_name, b_name);
|
||||
}
|
||||
|
||||
static struct symtab *lookup_existing_symbol(const char *var)
|
||||
{
|
||||
struct symtab *tmp, **lookup;
|
||||
struct symtab *result = NULL;
|
||||
|
||||
tmp = new_symtab_entry(var);
|
||||
if (!tmp) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
lookup = (struct symtab **) tfind(tmp, &my_symtab, (comparison_fn_t) &compare_symtabs);
|
||||
if (!lookup) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = (*lookup);
|
||||
|
||||
out:
|
||||
free_symtab(tmp);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/* add_boolean_var
|
||||
* creates copies of arguments, so caller can free them after use
|
||||
*/
|
||||
|
||||
int add_boolean_var(const char *var, int value)
|
||||
{
|
||||
struct symtab *n, **result;
|
||||
int rc = 0;
|
||||
|
||||
n = new_symtab_entry(var);
|
||||
if (!n) {
|
||||
rc = ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
n->type = sd_boolean;
|
||||
n->boolean = value;
|
||||
|
||||
result = (struct symtab **) tsearch(n, &my_symtab, (comparison_fn_t) &compare_symtabs);
|
||||
if (!result) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
rc = errno;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (*result != n) {
|
||||
/* already existing variable */
|
||||
PERROR("'%s' is already defined\n", var);
|
||||
rc = 1;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free_symtab(n);
|
||||
return rc;
|
||||
};
|
||||
|
||||
int get_boolean_var(const char *var)
|
||||
{
|
||||
struct symtab *result;
|
||||
int rc = 0;
|
||||
|
||||
result = lookup_existing_symbol(var);
|
||||
if (!result) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result->type != sd_boolean) {
|
||||
PERROR("Variable %s is not a boolean variable\n", var);
|
||||
rc = -2; /* XXX - might change this to specific values */
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = result->boolean;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int insert_set_var(struct symtab *var)
|
||||
{
|
||||
struct symtab **result;
|
||||
|
||||
result = (struct symtab **) tsearch(var, &my_symtab, (comparison_fn_t) &compare_symtabs);
|
||||
if (!result) {
|
||||
PERROR("Failed to allocate memory: %s\n", strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (*result != var) {
|
||||
/* already existing variable */
|
||||
PERROR("'%s' is already defined\n", var->var_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* new_set_var
|
||||
* creates copies of arguments, so caller can free them after use
|
||||
*/
|
||||
int new_set_var(const char *var, const char *value)
|
||||
{
|
||||
struct symtab *n;
|
||||
int rc = 0;
|
||||
|
||||
n = new_symtab_entry(var);
|
||||
if (!n) {
|
||||
rc = ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
n->type = sd_set;
|
||||
add_to_set(&(n->values), value);
|
||||
|
||||
rc = insert_set_var(n);
|
||||
if (! rc)
|
||||
return 0;
|
||||
|
||||
err:
|
||||
free_symtab(n);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* add_set_value
|
||||
* creates copies of arguments, so caller can free them after use
|
||||
*/
|
||||
int add_set_value(const char *var, const char *value)
|
||||
{
|
||||
struct symtab *result;
|
||||
int rc = 0;
|
||||
|
||||
result = lookup_existing_symbol(var);
|
||||
if (!result) {
|
||||
PERROR("Failed to find declaration for: %s\n", var);
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result->type != sd_set) {
|
||||
PERROR("Variable %s is not a set variable\n", var);
|
||||
rc = 2; /* XXX - might change this to specific values */
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(result->var_name, var) != 0) {
|
||||
PERROR("ASSERT: tfind found %s when looking up variable %s\n",
|
||||
result->var_name, var);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
add_to_set(&(result->values), value);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* returns a pointer to the value list, which should be used as the
|
||||
* argument to the get_next_set_value() function. */
|
||||
struct set_value *get_set_var(const char *var)
|
||||
{
|
||||
struct symtab *result;
|
||||
struct set_value *valuelist = NULL;
|
||||
|
||||
result = lookup_existing_symbol(var);
|
||||
if (!result) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result->type != sd_set) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(result->var_name, var) != 0) {
|
||||
PERROR("ASSERT: tfind found %s when looking up variable %s\n",
|
||||
result->var_name, var);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!result->expanded) {
|
||||
int err = __expand_variable(result);
|
||||
if (err) {
|
||||
PERROR("failure expanding variable %s\n", var);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
valuelist = result->expanded;
|
||||
out:
|
||||
return valuelist;
|
||||
}
|
||||
|
||||
/* iterator to walk the list of set values */
|
||||
char *get_next_set_value(struct set_value **list)
|
||||
{
|
||||
struct set_value *next;
|
||||
char *ret;
|
||||
|
||||
if (!list || !(*list))
|
||||
return NULL;
|
||||
|
||||
ret = (*list)->val;
|
||||
next = (*list)->next;
|
||||
(*list) = next;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct symtab *remove_set_var(const char *var_name)
|
||||
{
|
||||
struct symtab **result, *n, *var = NULL;
|
||||
|
||||
n = new_symtab_entry(var_name);
|
||||
if (!n) {
|
||||
//rc = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = (struct symtab **) tfind(n, &my_symtab, (comparison_fn_t) &compare_symtabs);
|
||||
if (!result) {
|
||||
/* XXX Warning? */
|
||||
//rc = ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
var = (*result);
|
||||
|
||||
result = (struct symtab **) tdelete(n, &my_symtab, (comparison_fn_t) &compare_symtabs);
|
||||
if (!result) {
|
||||
PERROR("ASSERT: delete_set_var: tfind found var %s but tdelete failed to delete it\n",
|
||||
var_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (var->type != sd_set) {
|
||||
PERROR("ASSERT: delete_set_var: deleting %s but is a boolean variable\n",
|
||||
var_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
out:
|
||||
free_symtab(n);
|
||||
|
||||
return var;
|
||||
}
|
||||
|
||||
/* delete_symbol
|
||||
* removes an individual variable from the symbol table. We don't
|
||||
* support this in the language, but for special variables that change
|
||||
* between profiles, we need this.
|
||||
*/
|
||||
int delete_set_var(const char *var_name)
|
||||
{
|
||||
struct symtab *var;
|
||||
|
||||
var = remove_set_var(var_name);
|
||||
if (var) {
|
||||
free_symtab(var);
|
||||
return 0;
|
||||
}
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
static void *seenlist = NULL;
|
||||
|
||||
static int is_seen(const char *var)
|
||||
{
|
||||
char **lookup;
|
||||
lookup = (char **) tfind(var, &seenlist, (comparison_fn_t) &strcmp);
|
||||
return (lookup != NULL);
|
||||
}
|
||||
|
||||
static void push_seen_var(const char *var)
|
||||
{
|
||||
char **lookup;
|
||||
lookup = (char **) tsearch(var, &seenlist, (comparison_fn_t) &strcmp);
|
||||
if (*lookup != var) {
|
||||
PERROR("ASSERT: '%s' is already in the seenlist\n", var);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void pop_seen_var(const char *var)
|
||||
{
|
||||
char **lookup;
|
||||
lookup = (char **) tdelete(var, &seenlist, (comparison_fn_t) &strcmp);
|
||||
if (lookup == NULL) {
|
||||
PERROR("ASSERT: popped var '%s' not found on the seenlist\n", var);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int __expand_variable(struct symtab *symbol)
|
||||
{
|
||||
struct set_value *list, *expanded = NULL;
|
||||
int retval = 0;
|
||||
struct var_string *split = NULL;
|
||||
|
||||
if (symbol->type == sd_boolean) {
|
||||
PERROR("Referenced variable %s is a boolean used in set context\n",
|
||||
symbol->var_name);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* already done */
|
||||
if (symbol->expanded)
|
||||
return 0;
|
||||
|
||||
push_seen_var(symbol->var_name);
|
||||
|
||||
for (list = symbol->values; list; list = list->next) {
|
||||
struct set_value *work_list = new_set_value(list->val);
|
||||
while (work_list) {
|
||||
struct symtab *ref;
|
||||
struct set_value *ref_item;
|
||||
struct set_value *t_value = work_list;
|
||||
int rc;
|
||||
|
||||
work_list = work_list->next;
|
||||
|
||||
split = split_out_var(t_value->val);
|
||||
if (!split) {
|
||||
/* fully expanded */
|
||||
add_to_set(&expanded, t_value->val);
|
||||
goto next;
|
||||
}
|
||||
|
||||
|
||||
if (is_seen(split->var)) {
|
||||
PERROR("Variable @{%s} is referenced recursively (by @{%s})\n",
|
||||
split->var, symbol->var_name);
|
||||
retval = 1;
|
||||
free_values(t_value);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ref = lookup_existing_symbol(split->var);
|
||||
if (!ref) {
|
||||
PERROR("Variable @{%s} references undefined variable @{%s}\n",
|
||||
symbol->var_name, split->var);
|
||||
retval = 3;
|
||||
free_values(t_value);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = __expand_variable(ref);
|
||||
if (rc != 0) {
|
||||
retval = rc;
|
||||
free_values(t_value);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ref->expanded) {
|
||||
PERROR("ASSERT: Variable @{%s} should have been expanded but isn't\n",
|
||||
split->var);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (ref_item = ref->expanded; ref_item; ref_item = ref_item->next) {
|
||||
char *expanded_string;
|
||||
if (!asprintf(&expanded_string, "%s%s%s",
|
||||
split->prefix ? split->prefix : "",
|
||||
ref_item->val,
|
||||
split->suffix ? split->suffix : "")) {
|
||||
PERROR("Out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
add_to_set(&work_list, expanded_string);
|
||||
free(expanded_string);
|
||||
}
|
||||
|
||||
next:
|
||||
t_value->next = NULL;
|
||||
free_values(t_value);
|
||||
free_var_string(split);
|
||||
}
|
||||
}
|
||||
|
||||
symbol->expanded = expanded;
|
||||
|
||||
out:
|
||||
pop_seen_var(symbol->var_name);
|
||||
free_var_string(split);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void expand_variable(const void *nodep, VISIT value, int level unused)
|
||||
{
|
||||
struct symtab **t = (struct symtab **) nodep;
|
||||
|
||||
if (value == preorder || value == endorder)
|
||||
return;
|
||||
|
||||
if ((*t)->type == sd_boolean)
|
||||
return;
|
||||
|
||||
__expand_variable(*t);
|
||||
}
|
||||
|
||||
void expand_variables(void)
|
||||
{
|
||||
twalk(my_symtab, &expand_variable);
|
||||
}
|
||||
|
||||
static inline void dump_set_values(struct set_value *value)
|
||||
{
|
||||
struct set_value *t = value;
|
||||
while (t) {
|
||||
printf(" \"%s\"", t->val);
|
||||
t = t->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void __dump_symtab_entry(struct symtab *entry, int do_expanded)
|
||||
{
|
||||
switch (entry->type) {
|
||||
case sd_boolean:
|
||||
printf("$%s = %s\n", entry->var_name,
|
||||
entry->boolean ? "true" : "false");
|
||||
break;
|
||||
case sd_set:
|
||||
printf("@%s =", entry->var_name);
|
||||
if (do_expanded) {
|
||||
if (!entry->expanded) {
|
||||
__expand_variable(entry);
|
||||
}
|
||||
dump_set_values(entry->expanded);
|
||||
} else {
|
||||
dump_set_values(entry->values);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
PERROR("ASSERT: unknown symbol table type for %s\n", entry->var_name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_symtab_entry(const void *nodep, VISIT value, int level unused)
|
||||
{
|
||||
struct symtab **t = (struct symtab **) nodep;
|
||||
|
||||
if (value == preorder || value == endorder)
|
||||
return;
|
||||
|
||||
__dump_symtab_entry(*t, 0);
|
||||
}
|
||||
|
||||
static void dump_expanded_symtab_entry(const void *nodep, VISIT value, int level unused)
|
||||
{
|
||||
struct symtab **t = (struct symtab **) nodep;
|
||||
|
||||
if (value == preorder || value == endorder)
|
||||
return;
|
||||
|
||||
__dump_symtab_entry(*t, 1);
|
||||
}
|
||||
|
||||
void dump_symtab(void)
|
||||
{
|
||||
twalk(my_symtab, &dump_symtab_entry);
|
||||
}
|
||||
|
||||
void dump_expanded_symtab(void)
|
||||
{
|
||||
twalk(my_symtab, &dump_expanded_symtab_entry);
|
||||
}
|
||||
|
||||
void free_symtabs(void)
|
||||
{
|
||||
if (my_symtab)
|
||||
tdestroy(my_symtab, (__free_fn_t)&free_symtab);
|
||||
my_symtab = NULL;
|
||||
}
|
||||
#include "symtab.h"
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
#include "unit_test.h"
|
||||
|
||||
int test_compare_symtab(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct symtab *a, *b, *c;
|
||||
|
||||
a = new_symtab_entry("blah");
|
||||
b = new_symtab_entry("suck");
|
||||
MY_TEST(a && b, "allocation test");
|
||||
|
||||
retval = compare_symtabs(a, b);
|
||||
MY_TEST(retval < 0, "comparison 1");
|
||||
|
||||
retval = compare_symtabs(b, a);
|
||||
MY_TEST(retval > 0, "comparison 2");
|
||||
|
||||
retval = compare_symtabs(b, a);
|
||||
MY_TEST(retval != 0, "comparison 3");
|
||||
|
||||
retval = compare_symtabs(b, b);
|
||||
MY_TEST(retval == 0, "comparison 4");
|
||||
|
||||
c = new_symtab_entry("blah");
|
||||
retval = compare_symtabs(a, c);
|
||||
MY_TEST(retval == 0, "comparison 5");
|
||||
|
||||
free_symtab(a);
|
||||
free_symtab(b);
|
||||
free_symtab(c);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_seenlist(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
MY_TEST(!is_seen("oogabooga"), "lookup unseen variable");
|
||||
|
||||
push_seen_var("oogabooga");
|
||||
MY_TEST(is_seen("oogabooga"), "lookup seen variable 1");
|
||||
MY_TEST(!is_seen("not_seen"), "lookup unseen variable 2");
|
||||
|
||||
push_seen_var("heebiejeebie");
|
||||
MY_TEST(is_seen("oogabooga"), "lookup seen variable 2");
|
||||
MY_TEST(is_seen("heebiejeebie"), "lookup seen variable 3");
|
||||
MY_TEST(!is_seen("not_seen"), "lookup unseen variable 3");
|
||||
|
||||
pop_seen_var("oogabooga");
|
||||
MY_TEST(!is_seen("oogabooga"), "lookup unseen variable 4");
|
||||
MY_TEST(is_seen("heebiejeebie"), "lookup seen variable 4");
|
||||
MY_TEST(!is_seen("not_seen"), "lookup unseen variable 5");
|
||||
|
||||
pop_seen_var("heebiejeebie");
|
||||
MY_TEST(!is_seen("heebiejeebie"), "lookup unseen variable 6");
|
||||
|
||||
//pop_seen_var("not_seen"); /* triggers assert */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_add_set_to_boolean(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
|
||||
struct value_list *val;
|
||||
/* test adding a set value to a boolean variable */
|
||||
retval = add_boolean_var("not_a_set_variable", 1);
|
||||
retval = symtab::add_var("@not_a_set_variable", 1);
|
||||
MY_TEST(retval == 0, "new boolean variable 3");
|
||||
retval = add_set_value("not_a_set_variable", "a set value");
|
||||
|
||||
val = new_value_list(strdup("a set value"));
|
||||
retval = symtab::add_set_value("@not_a_set_variable", val);
|
||||
MY_TEST(retval != 0, "add set value to boolean");
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
free_value_list(val);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -680,19 +53,19 @@ int test_expand_bool_within_set(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct symtab *retsym;
|
||||
variable *retsym;
|
||||
|
||||
/* test expanding a boolean var within a set variable */
|
||||
retval = add_boolean_var("not_a_set_variable", 1);
|
||||
retval = symtab::add_var("@not_a_set_variable", 1);
|
||||
MY_TEST(retval == 0, "new boolean variable 4");
|
||||
retval = new_set_var("set_variable", "set_value@{not_a_set_variable}");
|
||||
retval = symtab::add_var("set_variable", "set_value@{not_a_set_variable}");
|
||||
MY_TEST(retval == 0, "add set value with embedded boolean");
|
||||
retsym = lookup_existing_symbol("set_variable");
|
||||
retsym = symtab::lookup_existing_symbol("set_variable");
|
||||
MY_TEST(retsym != NULL, "get set variable w/boolean");
|
||||
retval = __expand_variable(retsym);
|
||||
retval = retsym->expand_variable();
|
||||
MY_TEST(retval != 0, "expand set variable with embedded boolean");
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -701,21 +74,21 @@ int test_expand_recursive_set_vars(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct symtab *retsym;
|
||||
variable *retsym;
|
||||
|
||||
/* test expanding a recursive var within a set variable */
|
||||
retval = new_set_var("recursive_1", "set_value@{recursive_2}");
|
||||
retval = symtab::add_var("recursive_1", "set_value@{recursive_2}");
|
||||
MY_TEST(retval == 0, "new recursive set variable 1");
|
||||
retval = new_set_var("recursive_2", "set_value@{recursive_3}");
|
||||
retval = symtab::add_var("recursive_2", "set_value@{recursive_3}");
|
||||
MY_TEST(retval == 0, "new recursive set variable 2");
|
||||
retval = new_set_var("recursive_3", "set_value@{recursive_1}");
|
||||
retval = symtab::add_var("recursive_3", "set_value@{recursive_1}");
|
||||
MY_TEST(retval == 0, "new recursive set variable 3");
|
||||
retsym = lookup_existing_symbol("recursive_1");
|
||||
retsym = symtab::lookup_existing_symbol("recursive_1");
|
||||
MY_TEST(retsym != NULL, "get recursive set variable");
|
||||
retval = __expand_variable(retsym);
|
||||
retval = retsym->expand_variable();
|
||||
MY_TEST(retval != 0, "expand recursive set variable");
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -724,17 +97,17 @@ int test_expand_undefined_set_var(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct symtab *retsym;
|
||||
variable *retsym;
|
||||
|
||||
/* test expanding an undefined var within a set variable */
|
||||
retval = new_set_var("defined_var", "set_value@{undefined_var}");
|
||||
retval = symtab::add_var("defined_var", "set_value@{undefined_var}");
|
||||
MY_TEST(retval == 0, "new undefined test set variable");
|
||||
retsym = lookup_existing_symbol("defined_var");
|
||||
retsym = symtab::lookup_existing_symbol("defined_var");
|
||||
MY_TEST(retsym != NULL, "get undefined test set variable");
|
||||
retval = __expand_variable(retsym);
|
||||
retval = retsym->expand_variable();
|
||||
MY_TEST(retval != 0, "expand undefined set variable");
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -743,20 +116,20 @@ int test_expand_set_var_during_dump(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct symtab *retsym;
|
||||
variable *retsym;
|
||||
|
||||
/* test expanding an defined var within a set variable during var dump*/
|
||||
retval = new_set_var("set_var_1", "set_value@{set_var_2}");
|
||||
retval = symtab::add_var("set_var_1", "set_value@{set_var_2}");
|
||||
MY_TEST(retval == 0, "new dump expansion set variable 1");
|
||||
retval = new_set_var("set_var_2", "some other set_value");
|
||||
retval = symtab::add_var("set_var_2", "some other set_value");
|
||||
MY_TEST(retval == 0, "new dump expansion set variable 2");
|
||||
retsym = lookup_existing_symbol("set_var_1");
|
||||
retsym = symtab::lookup_existing_symbol("set_var_1");
|
||||
MY_TEST(retsym != NULL, "get dump expansion set variable 1");
|
||||
__dump_symtab_entry(retsym, 0);
|
||||
__dump_symtab_entry(retsym, 1);
|
||||
__dump_symtab_entry(retsym, 0);
|
||||
retsym->dump(false);
|
||||
retsym->dump(true);
|
||||
retsym->dump(false);
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -765,13 +138,17 @@ int test_delete_set_var(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
variable *deleted;
|
||||
variable *retsym;
|
||||
|
||||
retval = new_set_var("deleteme", "delete this variable");
|
||||
retval = symtab::add_var("deleteme", "delete this variable");
|
||||
MY_TEST(retval == 0, "new delete set variable");
|
||||
retval = delete_set_var("deleteme");
|
||||
MY_TEST(retval == 0, "delete set variable");
|
||||
deleted = symtab::delete_var("deleteme");
|
||||
MY_TEST(deleted != NULL, "delete set variable");
|
||||
retsym = symtab::lookup_existing_symbol(deleted->var_name.c_str());
|
||||
MY_TEST(retsym == NULL, "deleteme was deleted from symtable");
|
||||
|
||||
free_symtabs();
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -780,13 +157,12 @@ int main(void)
|
||||
{
|
||||
int rc = 0;
|
||||
int retval;
|
||||
struct set_value *retptr;
|
||||
struct value_list *list;
|
||||
struct value_list *val;
|
||||
variable *retsym;
|
||||
|
||||
rc = test_compare_symtab();
|
||||
|
||||
retval = test_seenlist();
|
||||
if (rc == 0)
|
||||
rc = retval;
|
||||
val = new_value_list(strdup("a set value"));
|
||||
retval = symtab::add_set_value("@not_a_set_variable", val);
|
||||
|
||||
retval = test_add_set_to_boolean();
|
||||
if (rc == 0)
|
||||
@ -812,76 +188,94 @@ int main(void)
|
||||
if (rc == 0)
|
||||
rc = retval;
|
||||
|
||||
retval = new_set_var("test", "test value");
|
||||
retval = symtab::add_var("test", "test value");
|
||||
MY_TEST(retval == 0, "new set variable 1");
|
||||
|
||||
retval = new_set_var("test", "different value");
|
||||
retval = symtab::add_var("test", "different value");
|
||||
MY_TEST(retval != 0, "new set variable 2");
|
||||
|
||||
retval = new_set_var("testing", "testing");
|
||||
retval = symtab::add_var("testing", "testing");
|
||||
MY_TEST(retval == 0, "new set variable 3");
|
||||
|
||||
retval = new_set_var("monopuff", "Mockingbird");
|
||||
retval = symtab::add_var("monopuff", "Mockingbird");
|
||||
MY_TEST(retval == 0, "new set variable 4");
|
||||
|
||||
retval = new_set_var("stereopuff", "Unsupervised");
|
||||
retval = symtab::add_var("stereopuff", "Unsupervised");
|
||||
MY_TEST(retval == 0, "new set variable 5");
|
||||
|
||||
retval = add_set_value("stereopuff", "Fun to Steal");
|
||||
val = new_value_list(strdup("Fun to Steal"));
|
||||
list = val;
|
||||
retval = symtab::add_set_value("@stereopuff", val);
|
||||
MY_TEST(retval == 0, "add set value 1");
|
||||
|
||||
retval = add_set_value("stereopuff", "/in/direction");
|
||||
val = new_value_list(strdup("/in/direction"));
|
||||
list_append(list, val);
|
||||
retval = symtab::add_set_value("@stereopuff", val);
|
||||
MY_TEST(retval == 0, "add set value 2");
|
||||
|
||||
retval = add_set_value("no_such_variable", "stereopuff");
|
||||
val = new_value_list(strdup("stereopuff"));
|
||||
list_append(list, val);
|
||||
retval = symtab::add_set_value("@no_such_variable", val);
|
||||
MY_TEST(retval != 0, "add to non-existent set var");
|
||||
|
||||
retval = add_boolean_var("abuse", 0);
|
||||
retval = symtab::add_var("@abuse", 0);
|
||||
MY_TEST(retval == 0, "new boolean variable 1");
|
||||
|
||||
retval = add_boolean_var("abuse", 1);
|
||||
retval = symtab::add_var("@abuse", 1);
|
||||
MY_TEST(retval != 0, "duplicate boolean variable 1");
|
||||
|
||||
retval = add_boolean_var("stereopuff", 1);
|
||||
retval = symtab::add_var("@stereopuff", 1);
|
||||
MY_TEST(retval != 0, "duplicate boolean variable 2");
|
||||
|
||||
retval = add_boolean_var("shenanigan", 1);
|
||||
retval = symtab::add_var("@shenanigan", 1);
|
||||
MY_TEST(retval == 0, "new boolean variable 2");
|
||||
|
||||
retval = get_boolean_var("shenanigan");
|
||||
MY_TEST(retval == 1, "get boolean variable 1");
|
||||
retsym = symtab::get_boolean_var("@shenanigan");
|
||||
MY_TEST(retsym != NULL, "boolean variable 1 exists");
|
||||
MY_TEST(retsym->boolean == 1, "get boolean variable 1");
|
||||
|
||||
retval = get_boolean_var("abuse");
|
||||
MY_TEST(retval == 0, "get boolean variable 2");
|
||||
retsym = symtab::get_boolean_var("@abuse");
|
||||
MY_TEST(retsym != NULL, "boolean variable 2 exists");
|
||||
MY_TEST(retsym->boolean == 0, "get boolean variable 2");
|
||||
|
||||
retval = get_boolean_var("non_existant");
|
||||
MY_TEST(retval < 0, "get nonexistent boolean variable");
|
||||
retsym = symtab::get_boolean_var("@non_existant");
|
||||
MY_TEST(retsym == NULL, "get nonexistent boolean variable");
|
||||
|
||||
retval = get_boolean_var("stereopuff");
|
||||
MY_TEST(retval < 0, "get boolean variable that's declared a set var");
|
||||
retsym = symtab::get_boolean_var("@stereopuff");
|
||||
MY_TEST(retsym == NULL, "get boolean variable that's declared a set var");
|
||||
|
||||
retptr = get_set_var("daves_not_here_man");
|
||||
MY_TEST(retptr == NULL, "get nonexistent set variable");
|
||||
retsym = symtab::get_set_var("@daves_not_here_man");
|
||||
MY_TEST(retsym == NULL, "get nonexistent set variable");
|
||||
|
||||
retptr = get_set_var("abuse");
|
||||
MY_TEST(retptr == NULL, "get set variable that's declared a boolean");
|
||||
retsym = symtab::get_set_var("@abuse");
|
||||
MY_TEST(retsym == NULL, "get set variable that's declared a boolean");
|
||||
|
||||
/* test walking set values */
|
||||
retptr = get_set_var("monopuff");
|
||||
MY_TEST(retptr != NULL, "get set variable 1");
|
||||
retval = strcmp(get_next_set_value(&retptr), "Mockingbird");
|
||||
MY_TEST(retval == 0, "get set value 1");
|
||||
MY_TEST(get_next_set_value(&retptr) == NULL, "get no more set values 1");
|
||||
retsym = symtab::get_set_var("@monopuff");
|
||||
MY_TEST(retsym != NULL, "get set variable 1");
|
||||
MY_TEST(retsym->values.size() == 1, "only one value");
|
||||
MY_TEST(retsym->expanded.size() == 1, "only one expanded");
|
||||
|
||||
retval = new_set_var("eek", "Mocking@{monopuff}bir@{stereopuff}d@{stereopuff}");
|
||||
for (std::string value : retsym->values) {
|
||||
retval = strcmp(value.c_str(), "Mockingbird");
|
||||
MY_TEST(retval == 0, "get set value 1");
|
||||
}
|
||||
|
||||
for (std::string value : retsym->expanded) {
|
||||
retval = strcmp(value.c_str(), "Mockingbird");
|
||||
MY_TEST(retval == 0, "get set value 1 expanded");
|
||||
}
|
||||
|
||||
retval = symtab::add_var("eek", "Mocking@{monopuff}bir@{stereopuff}d@{stereopuff}");
|
||||
MY_TEST(retval == 0, "new set variable 4");
|
||||
|
||||
dump_symtab();
|
||||
expand_variables();
|
||||
dump_symtab();
|
||||
dump_expanded_symtab();
|
||||
symtab::dump(false);
|
||||
symtab::expand_variables();
|
||||
symtab::dump(false);
|
||||
symtab::dump(true);
|
||||
|
||||
free_symtabs();
|
||||
free_value_list(list);
|
||||
symtab::free_symtab();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -31,213 +31,12 @@
|
||||
#include "profile.h"
|
||||
#include "mount.h"
|
||||
#include "dbus.h"
|
||||
|
||||
static inline const char *get_var_end(const char *var)
|
||||
{
|
||||
const char *eptr = var;
|
||||
|
||||
while (*eptr) {
|
||||
if (*eptr == '}')
|
||||
return eptr;
|
||||
/* first character must be alpha */
|
||||
if (eptr == var) {
|
||||
if (!isalpha(*eptr))
|
||||
return NULL; /* invalid char */
|
||||
} else {
|
||||
if (!(*eptr == '_' || isalnum(*eptr)))
|
||||
return NULL; /* invalid char */
|
||||
}
|
||||
eptr++;
|
||||
}
|
||||
return NULL; /* no terminating '}' */
|
||||
}
|
||||
|
||||
static struct var_string *split_string(const char *string, const char *var_begin,
|
||||
const char *var_end)
|
||||
{
|
||||
struct var_string *n = (struct var_string *) calloc(1, sizeof(struct var_string));
|
||||
unsigned int offset = strlen("@{");
|
||||
if (!n) {
|
||||
PERROR("Memory allocation error\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (var_begin != string) {
|
||||
n->prefix = strndup(string, var_begin - string);
|
||||
}
|
||||
|
||||
n->var = strndup(var_begin + offset, var_end - (var_begin + offset));
|
||||
|
||||
if (strlen(var_end + 1) != 0) {
|
||||
n->suffix = strdup(var_end + 1);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct var_string *split_out_var(const char *string)
|
||||
{
|
||||
struct var_string *n = NULL;
|
||||
const char *sptr;
|
||||
bool bEscape = false; /* flag to indicate escape */
|
||||
|
||||
if (!string) /* shouldn't happen */
|
||||
return NULL;
|
||||
|
||||
sptr = string;
|
||||
|
||||
while (!n && *sptr) {
|
||||
switch (*sptr) {
|
||||
case '\\':
|
||||
bEscape = !bEscape;
|
||||
break;
|
||||
case '@':
|
||||
if (bEscape) {
|
||||
bEscape = false;
|
||||
} else if (*(sptr + 1) == '{') {
|
||||
const char *eptr = get_var_end(sptr + 2);
|
||||
if (!eptr)
|
||||
break; /* no variable end found */
|
||||
if (eptr == sptr + 2) {
|
||||
/* XXX - better diagnostics here, please */
|
||||
PERROR("Empty variable name found!\n");
|
||||
exit(1);
|
||||
}
|
||||
n = split_string(string, sptr, eptr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bEscape = false;
|
||||
}
|
||||
sptr++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void free_var_string(struct var_string *var)
|
||||
{
|
||||
if (!var)
|
||||
return;
|
||||
if (var->prefix)
|
||||
free(var->prefix);
|
||||
if (var->var)
|
||||
free(var->var);
|
||||
if (var->suffix)
|
||||
free(var->suffix);
|
||||
free(var);
|
||||
}
|
||||
|
||||
static void trim_trailing_slash(std::string& str)
|
||||
{
|
||||
std::size_t found = str.find_last_not_of('/');
|
||||
if (found != std::string::npos)
|
||||
str.erase(found + 1);
|
||||
else
|
||||
str.clear(); // str is all '/'
|
||||
}
|
||||
|
||||
static void write_replacement(const char separator, const char* value,
|
||||
std::string& replacement, bool filter_leading_slash,
|
||||
bool filter_trailing_slash)
|
||||
{
|
||||
const char *p = value;
|
||||
|
||||
replacement.append(1, separator);
|
||||
|
||||
if (filter_leading_slash)
|
||||
while (*p == '/')
|
||||
p++;
|
||||
|
||||
replacement.append(p);
|
||||
if (filter_trailing_slash)
|
||||
trim_trailing_slash(replacement);
|
||||
}
|
||||
|
||||
static int expand_by_alternations(struct set_value **valuelist,
|
||||
struct var_string *split_var,
|
||||
char **name)
|
||||
{
|
||||
char *value, *first_value;
|
||||
std::string replacement;
|
||||
bool filter_leading_slash = false;
|
||||
bool filter_trailing_slash = false;
|
||||
|
||||
first_value = get_next_set_value(valuelist);
|
||||
if (!first_value) {
|
||||
PERROR("ASSERT: set variable (%s) should always have at least one value assigned to it\n",
|
||||
split_var->var);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(*name);
|
||||
|
||||
value = get_next_set_value(valuelist);
|
||||
if (!value) {
|
||||
/* only one entry for the variable, so just sub it in */
|
||||
if (asprintf(name, "%s%s%s",
|
||||
split_var->prefix ? split_var->prefix : "",
|
||||
first_value,
|
||||
split_var->suffix ? split_var->suffix : "") == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (split_var->prefix && split_var->prefix[strlen(split_var->prefix) - 1] == '/')
|
||||
filter_leading_slash = true;
|
||||
if (split_var->suffix && *split_var->suffix == '/')
|
||||
filter_trailing_slash = true;
|
||||
|
||||
write_replacement('{', first_value, replacement, filter_leading_slash, filter_trailing_slash);
|
||||
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
|
||||
|
||||
while ((value = get_next_set_value(valuelist))) {
|
||||
write_replacement(',', value, replacement, filter_leading_slash, filter_trailing_slash);
|
||||
}
|
||||
|
||||
if (asprintf(name, "%s%s}%s",
|
||||
split_var->prefix ? split_var->prefix : "",
|
||||
replacement.c_str(),
|
||||
split_var->suffix ? split_var->suffix : "") == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include "symtab.h"
|
||||
|
||||
/* doesn't handle variables in options atm */
|
||||
int expand_entry_variables(char **name)
|
||||
{
|
||||
struct set_value *valuelist;
|
||||
struct var_string *split_var;
|
||||
int ret;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (!*name) /* can happen when entry is optional */
|
||||
return 0;
|
||||
|
||||
while ((split_var = split_out_var(*name))) {
|
||||
valuelist = get_set_var(split_var->var);
|
||||
if (!valuelist) {
|
||||
int boolean = get_boolean_var(split_var->var);
|
||||
if (boolean == -1)
|
||||
PERROR("Found reference to variable %s, but is never declared\n",
|
||||
split_var->var);
|
||||
else
|
||||
PERROR("Found reference to set variable %s, but declared boolean\n",
|
||||
split_var->var);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = expand_by_alternations(&valuelist, split_var, name);
|
||||
|
||||
free_var_string(split_var);
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
}
|
||||
return 0;
|
||||
return variable::expand_by_alternation(name);
|
||||
}
|
||||
|
||||
static int process_variables_in_entries(struct cod_entry *entry_list)
|
||||
@ -338,9 +137,9 @@ static std::string escape_re(std::string str)
|
||||
|
||||
int process_profile_variables(Profile *prof)
|
||||
{
|
||||
int error = 0, rc;
|
||||
struct symtab *saved_exec_path = NULL;
|
||||
struct symtab *saved_attach_path = NULL;
|
||||
int error = 0;
|
||||
variable *saved_exec_path = NULL;
|
||||
variable *saved_attach_path = NULL;
|
||||
|
||||
/* needs to be before PROFILE_NAME_VARIABLE so that variable will
|
||||
* have the correct name
|
||||
@ -353,7 +152,7 @@ int process_profile_variables(Profile *prof)
|
||||
/* escape profile name elements that could be interpreted as
|
||||
* regular expressions.
|
||||
*/
|
||||
error = new_set_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
|
||||
error = symtab::add_var(PROFILE_NAME_VARIABLE, escape_re(prof->get_name(false)).c_str());
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
@ -364,13 +163,13 @@ int process_profile_variables(Profile *prof)
|
||||
* the attachment.
|
||||
*/
|
||||
/* need to take into account alias, but not yet */
|
||||
saved_attach_path = remove_set_var(PROFILE_ATTACH_VAR);
|
||||
error = new_set_var(PROFILE_ATTACH_VAR, prof->attachment);
|
||||
saved_attach_path = symtab::delete_var(PROFILE_ATTACH_VAR);
|
||||
error = symtab::add_var(PROFILE_ATTACH_VAR, (const char*) prof->attachment);
|
||||
if (error)
|
||||
goto cleanup_name;
|
||||
/* update to use kernel vars if available */
|
||||
saved_exec_path = remove_set_var(PROFILE_EXEC_VAR);
|
||||
error = new_set_var(PROFILE_EXEC_VAR, prof->attachment);
|
||||
saved_exec_path = symtab::delete_var(PROFILE_EXEC_VAR);
|
||||
error = symtab::add_var(PROFILE_EXEC_VAR, (const char*) prof->attachment);
|
||||
if (error)
|
||||
goto cleanup_attach;
|
||||
}
|
||||
@ -386,24 +185,18 @@ cleanup:
|
||||
* don't support that yet.
|
||||
*/
|
||||
if (prof->attachment) {
|
||||
rc = delete_set_var(PROFILE_EXEC_VAR);
|
||||
if (!error)
|
||||
error = rc;
|
||||
symtab::delete_var(PROFILE_EXEC_VAR);
|
||||
if (saved_exec_path)
|
||||
insert_set_var(saved_exec_path);
|
||||
symtab::add_var(*saved_exec_path);
|
||||
}
|
||||
cleanup_attach:
|
||||
if (prof->attachment) {
|
||||
rc = delete_set_var(PROFILE_ATTACH_VAR);
|
||||
if (!error)
|
||||
error = rc;
|
||||
symtab::delete_var(PROFILE_ATTACH_VAR);
|
||||
if (saved_attach_path)
|
||||
insert_set_var(saved_attach_path);
|
||||
symtab::add_var(*saved_attach_path);
|
||||
}
|
||||
cleanup_name:
|
||||
rc = delete_set_var(PROFILE_NAME_VARIABLE);
|
||||
if (!error)
|
||||
error = rc;
|
||||
symtab::delete_var(PROFILE_NAME_VARIABLE);
|
||||
|
||||
out:
|
||||
return error;
|
||||
@ -413,82 +206,75 @@ out:
|
||||
|
||||
#include "unit_test.h"
|
||||
|
||||
int test_get_var_end(void)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *retchar;
|
||||
const char *testchar;
|
||||
|
||||
testchar = "TRUE}";
|
||||
retchar = get_var_end(testchar);
|
||||
MY_TEST(retchar - testchar == strlen("TRUE"), "get var end for TRUE}");
|
||||
|
||||
testchar = "some_var}some other text";
|
||||
retchar = get_var_end(testchar);
|
||||
MY_TEST(retchar - testchar == strlen("some_var"), "get var end for some_var}");
|
||||
|
||||
testchar = "some_var}some other} text";
|
||||
retchar = get_var_end(testchar);
|
||||
MY_TEST(retchar - testchar == strlen("some_var"), "get var end for some_var} 2");
|
||||
|
||||
testchar = "FALSE";
|
||||
retchar = get_var_end(testchar);
|
||||
MY_TEST(retchar == NULL, "get var end for FALSE");
|
||||
|
||||
testchar = "pah,pah}pah ";
|
||||
retchar = get_var_end(testchar);
|
||||
MY_TEST(retchar == NULL, "get var end for pah,pah}");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_split_string(void)
|
||||
{
|
||||
int rc = 0;
|
||||
char *tst_string, *var_start, *var_end;
|
||||
struct var_string *ret_struct;
|
||||
char *tst_string;
|
||||
const char *prefix = "abcdefg";
|
||||
const char *var = "boogie";
|
||||
const char *suffix = "suffixication";
|
||||
std::tuple<std::string, std::string, std::string> result;
|
||||
std::string result_prefix;
|
||||
std::string result_var;
|
||||
std::string result_suffix;
|
||||
char *pvar;
|
||||
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
|
||||
var_start = tst_string + strlen(prefix);
|
||||
var_end = var_start + strlen(var) + strlen("@\{");
|
||||
ret_struct = split_string(tst_string, var_start, var_end);
|
||||
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split string 1 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 1 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split string 1 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split string 1 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "split string 1 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split string 1 suffix");
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
asprintf(&tst_string, "@{%s}%s", var, suffix);
|
||||
var_start = tst_string;
|
||||
var_end = var_start + strlen(var) + strlen("@\{");
|
||||
ret_struct = split_string(tst_string, var_start, var_end);
|
||||
MY_TEST(ret_struct->prefix == NULL, "split string 2 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 2 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split string 2 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(result_prefix.empty(), "split string 2 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "split string 2 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split string 2 suffix");
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
asprintf(&tst_string, "%s@{%s}", prefix, var);
|
||||
var_start = tst_string + strlen(prefix);
|
||||
var_end = var_start + strlen(var) + strlen("@\{");
|
||||
ret_struct = split_string(tst_string, var_start, var_end);
|
||||
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split string 3 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 3 var");
|
||||
MY_TEST(ret_struct->suffix == NULL, "split string 3 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split string 3 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "split string 3 var");
|
||||
MY_TEST(result_suffix.empty(), "split string 3 suffix");
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
asprintf(&tst_string, "@{%s}", var);
|
||||
var_start = tst_string;
|
||||
var_end = var_start + strlen(var) + strlen("@\{");
|
||||
ret_struct = split_string(tst_string, var_start, var_end);
|
||||
MY_TEST(ret_struct->prefix == NULL, "split string 4 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split string 4 var");
|
||||
MY_TEST(ret_struct->suffix == NULL, "split string 4 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(result_prefix.empty(), "split string 4 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "split string 4 var");
|
||||
MY_TEST(result_suffix.empty(), "split string 4 suffix");
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
asprintf(&tst_string, "%s%s%s", prefix, var, suffix);;
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
MY_TEST(result_prefix.empty(), "split string 5 prefix");
|
||||
MY_TEST(result_var.empty(), "split string 5 var");
|
||||
MY_TEST(result_suffix.empty(), "split string 5 suffix");
|
||||
free(tst_string);
|
||||
|
||||
return rc;
|
||||
@ -498,135 +284,228 @@ int test_split_out_var(void)
|
||||
{
|
||||
int rc = 0;
|
||||
char *tst_string, *tmp;
|
||||
struct var_string *ret_struct;
|
||||
const char *prefix = "abcdefg";
|
||||
const char *var = "boogie";
|
||||
const char *var2 = "V4rW1thNum5";
|
||||
const char *var3 = "boogie_board";
|
||||
const char *suffix = "suffixication";
|
||||
std::tuple<std::string, std::string, std::string> result;
|
||||
std::string result_prefix;
|
||||
std::string result_var;
|
||||
std::string result_suffix;
|
||||
char *pvar = NULL;
|
||||
|
||||
/* simple case */
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 1 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 1 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 1 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable 1 pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 1 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 1 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 1 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* no prefix */
|
||||
asprintf(&tst_string, "@{%s}%s", var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct->prefix == NULL, "split out var 2 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 2 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 2 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable 2 pvar");
|
||||
MY_TEST(result_prefix.empty(), "extract_variable 2 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 2 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 2 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* no suffix */
|
||||
asprintf(&tst_string, "%s@{%s}", prefix, var);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 3 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 3 var");
|
||||
MY_TEST(ret_struct->suffix == NULL, "split out var 3 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable 3 pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 3 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 3 var");
|
||||
MY_TEST(result_suffix.empty(), "extract_variable 3 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* var only */
|
||||
asprintf(&tst_string, "@{%s}", var);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct->prefix == NULL, "split out var 4 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 4 var");
|
||||
MY_TEST(ret_struct->suffix == NULL, "split out var 4 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable 4 pvar");
|
||||
MY_TEST(result_prefix.empty(), "extract_variable 4 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 4 var");
|
||||
MY_TEST(result_suffix.empty(), "extract_variable 4 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* quoted var, shouldn't split */
|
||||
asprintf(&tst_string, "%s\\@{%s}%s", prefix, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - quoted @");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
MY_TEST(result_prefix.empty(), "extract_variable - quoted @ prefix");
|
||||
MY_TEST(result_var.empty(), "extract_variable - quoted @ var");
|
||||
MY_TEST(result_suffix.empty(), "extract_variable - quoted @ suffix");
|
||||
free(tst_string);
|
||||
|
||||
/* quoted \, split should succeed */
|
||||
asprintf(&tst_string, "%s\\\\@{%s}%s", prefix, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
tmp = strndup(tst_string, strlen(prefix) + 2);
|
||||
MY_TEST(strcmp(ret_struct->prefix, tmp) == 0, "split out var 5 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 5 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 5 suffix");
|
||||
free_var_string(ret_struct);
|
||||
MY_TEST(pvar != NULL, "extract_variable 5 pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), tmp) == 0, "extract_variable 5 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 5 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 5 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
free(tmp);
|
||||
|
||||
/* un terminated var, should fail */
|
||||
asprintf(&tst_string, "%s@{%s%s", prefix, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - un-terminated var");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
MY_TEST(result_prefix.empty(), "extract_variable - un-terminated var prefix");
|
||||
MY_TEST(result_var.empty(), "extract_variable - un-terminated var var");
|
||||
MY_TEST(result_suffix.empty(), "extract_variable - un-terminated var suffix");
|
||||
free(tst_string);
|
||||
|
||||
/* invalid char in var, should fail */
|
||||
asprintf(&tst_string, "%s@{%s^%s}%s", prefix, var, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - invalid char in var");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar == NULL, "process_var - invalid char in var");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* two vars, should only strip out first */
|
||||
asprintf(&tmp, "@{%s}%s}", suffix, suffix);
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var, tmp);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(strcmp(ret_struct->prefix, prefix) == 0, "split out var 6 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 6 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, tmp) == 0, "split out var 6 suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable 6 pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "extract_variable 6 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 6 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), tmp) == 0, "extract_variable 6 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
free(tmp);
|
||||
|
||||
/* quoted @ followed by var, split should succeed */
|
||||
asprintf(&tst_string, "%s\\@@{%s}%s", prefix, var, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
tmp = strndup(tst_string, strlen(prefix) + 2);
|
||||
MY_TEST(strcmp(ret_struct->prefix, tmp) == 0, "split out var 7 prefix");
|
||||
MY_TEST(strcmp(ret_struct->var, var) == 0, "split out var 7 var");
|
||||
MY_TEST(strcmp(ret_struct->suffix, suffix) == 0, "split out var 7 suffix");
|
||||
free_var_string(ret_struct);
|
||||
MY_TEST(pvar != NULL, "extract_variable 7 pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), tmp) == 0, "extract_variable 7 prefix");
|
||||
MY_TEST(strcmp(pvar, var) == 0, "extract_variable 7 var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "extract_variable 7 suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
free(tmp);
|
||||
|
||||
/* numeric char in var, should succeed */
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var2, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out numeric var prefix");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->var, var2) == 0, "split numeric var var");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out numeric var suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable numeric var pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split out numeric var prefix");
|
||||
MY_TEST(strcmp(pvar, var2) == 0, "split numeric var var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split out numeric var suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* numeric first char in var, should fail */
|
||||
asprintf(&tst_string, "%s@{6%s}%s", prefix, var2, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - numeric first char in var");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar == NULL, "process_var - invalid char in var");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* underscore char in var, should succeed */
|
||||
asprintf(&tst_string, "%s@{%s}%s", prefix, var3, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->prefix, prefix) == 0, "split out underscore var prefix");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->var, var3) == 0, "split out underscore var");
|
||||
MY_TEST(ret_struct && strcmp(ret_struct->suffix, suffix) == 0, "split out underscore var suffix");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar != NULL, "extract_variable underscore var pvar");
|
||||
MY_TEST(strcmp(result_prefix.c_str(), prefix) == 0, "split out underscore var prefix");
|
||||
MY_TEST(strcmp(pvar, var3) == 0, "split out underscore var");
|
||||
MY_TEST(strcmp(result_suffix.c_str(), suffix) == 0, "split out underscore var suffix");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* underscore first char in var, should fail */
|
||||
asprintf(&tst_string, "%s@{_%s%s}%s", prefix, var2, var3, suffix);
|
||||
ret_struct = split_out_var(tst_string);
|
||||
MY_TEST(ret_struct == NULL, "split out var - underscore first char in var");
|
||||
free_var_string(ret_struct);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar == NULL, "process_var - invalid char in var");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
/* empty var name, should fail */
|
||||
asprintf(&tst_string, "%s@{}%s", prefix, suffix);
|
||||
result = extract_variable(tst_string);
|
||||
result_prefix = std::get<0>(result);
|
||||
result_var = std::get<1>(result);
|
||||
result_suffix = std::get<2>(result);
|
||||
pvar = variable::process_var(result_var.c_str());
|
||||
MY_TEST(pvar == NULL, "process_var - empty var name");
|
||||
if (pvar)
|
||||
free(pvar);
|
||||
free(tst_string);
|
||||
|
||||
return rc;
|
||||
}
|
||||
int main(void)
|
||||
@ -634,10 +513,6 @@ int main(void)
|
||||
int rc = 0;
|
||||
int retval;
|
||||
|
||||
retval = test_get_var_end();
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
||||
retval = test_split_string();
|
||||
if (retval != 0)
|
||||
rc = retval;
|
||||
|
@ -193,6 +193,7 @@ static void abi_features(char *filename, bool search);
|
||||
#include "network.h"
|
||||
#include "all_rule.h"
|
||||
#include "cond_expr.h"
|
||||
#include "symtab.h"
|
||||
}
|
||||
|
||||
%union {
|
||||
@ -492,71 +493,37 @@ alias: TOK_ALIAS TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
|
||||
varassign: TOK_SET_VAR TOK_EQUALS valuelist
|
||||
{
|
||||
struct value_list *list = $3;
|
||||
char *var_name = process_var($1);
|
||||
int err;
|
||||
if (!list || !list->value)
|
||||
yyerror("Assert: valuelist returned NULL");
|
||||
PDEBUG("Matched: set assignment for (%s)\n", $1);
|
||||
err = new_set_var(var_name, list->value);
|
||||
err = symtab::add_var($1, $3);
|
||||
if (err) {
|
||||
free(var_name);
|
||||
yyerror("variable %s was previously declared", $1);
|
||||
/* FIXME: it'd be handy to report the previous location */
|
||||
}
|
||||
for (list = list->next; list; list = list->next) {
|
||||
err = add_set_value(var_name, list->value);
|
||||
if (err) {
|
||||
free(var_name);
|
||||
yyerror("Error adding %s to set var %s",
|
||||
list->value, $1);
|
||||
}
|
||||
}
|
||||
|
||||
free_value_list($3);
|
||||
free(var_name);
|
||||
free($1);
|
||||
}
|
||||
|
||||
varassign: TOK_SET_VAR TOK_ADD_ASSIGN valuelist
|
||||
{
|
||||
struct value_list *list = $3;
|
||||
char *var_name = process_var($1);
|
||||
int err;
|
||||
if (!list || !list->value)
|
||||
yyerror("Assert: valuelist returned NULL");
|
||||
PDEBUG("Matched: additive assignment for (%s)\n", $1);
|
||||
/* do the first one outside the loop, subsequent
|
||||
* failures are indicative of symtab failures */
|
||||
err = add_set_value(var_name, list->value);
|
||||
err = symtab::add_set_value($1, $3);
|
||||
if (err) {
|
||||
free(var_name);
|
||||
yyerror("variable %s was not previously declared, but is being assigned additional values", $1);
|
||||
}
|
||||
for (list = list->next; list; list = list->next) {
|
||||
err = add_set_value(var_name, list->value);
|
||||
if (err) {
|
||||
free(var_name);
|
||||
yyerror("Error adding %s to set var %s",
|
||||
list->value, $1);
|
||||
}
|
||||
}
|
||||
free_value_list($3);
|
||||
free(var_name);
|
||||
free($1);
|
||||
}
|
||||
|
||||
varassign: TOK_BOOL_VAR TOK_EQUALS TOK_VALUE
|
||||
{
|
||||
int boolean, err;
|
||||
char *var_name = process_var($1);
|
||||
PDEBUG("Matched: boolean assignment (%s) to %s\n", $1, $3);
|
||||
boolean = str_to_boolean($3);
|
||||
if (boolean == -1) {
|
||||
yyerror("Invalid boolean assignment for (%s): %s is not true or false",
|
||||
$1, $3);
|
||||
}
|
||||
err = add_boolean_var(var_name, boolean);
|
||||
free(var_name);
|
||||
err = symtab::add_var($1, boolean);
|
||||
if (err) {
|
||||
yyerror("variable %s was previously declared", $1);
|
||||
/* FIXME: it'd be handy to report the previous location */
|
||||
|
167
parser/symtab.cc
Normal file
167
parser/symtab.cc
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2025
|
||||
* Canonical Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#include "symtab.h"
|
||||
|
||||
template <typename T>
|
||||
int symtab::add_var(const char *var, T value)
|
||||
{
|
||||
char *var_name = variable::process_var(var);
|
||||
if (!var_name)
|
||||
return 1;
|
||||
|
||||
const auto search = my_symtab.find(var_name);
|
||||
if (search != my_symtab.end()) {
|
||||
/* already existing variable */
|
||||
PERROR("'%s' is already defined\n", var);
|
||||
free(var_name);
|
||||
return 1;
|
||||
}
|
||||
my_symtab.emplace(var_name, variable(var_name, value));
|
||||
free(var_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int symtab::add_var(const char *var, int value)
|
||||
{
|
||||
return add_var<int>(var, value);
|
||||
}
|
||||
|
||||
int symtab::add_var(const char *var, struct value_list *value)
|
||||
{
|
||||
return add_var<struct value_list *>(var, value);
|
||||
}
|
||||
|
||||
int symtab::add_var(const char *var_name, const char *value)
|
||||
{
|
||||
/* note that here var_name must be processed already */
|
||||
const auto search = my_symtab.find(var_name);
|
||||
if (search != my_symtab.end()) {
|
||||
/* already existing variable */
|
||||
PERROR("'%s' is already defined\n", var_name);
|
||||
return 1;
|
||||
}
|
||||
my_symtab.emplace(var_name, variable(var_name, value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int symtab::add_var(variable var)
|
||||
{
|
||||
const auto search = my_symtab.find(var.var_name);
|
||||
if (search != my_symtab.end()) {
|
||||
/* already existing variable */
|
||||
PERROR("'%s' is already defined\n", var.var_name.c_str());
|
||||
return 1;
|
||||
}
|
||||
my_symtab.emplace(var.var_name, var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
variable *symtab::lookup_existing_symbol(const char *var_name)
|
||||
{
|
||||
if (!var_name)
|
||||
return nullptr;
|
||||
|
||||
const auto var = my_symtab.find(var_name);
|
||||
if (var == my_symtab.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(var->second);
|
||||
}
|
||||
|
||||
int symtab::add_set_value(const char *var_name, struct value_list *value)
|
||||
{
|
||||
char *pvar_name = variable::process_var(var_name);
|
||||
variable *var = lookup_existing_symbol(pvar_name);
|
||||
if (!var) {
|
||||
PERROR("Failed to find declaration for: %s\n", pvar_name);
|
||||
free(pvar_name);
|
||||
return 1;
|
||||
}
|
||||
free(pvar_name);
|
||||
return var->add_set_value(value);
|
||||
}
|
||||
|
||||
variable *symtab::delete_var(const char *var_name)
|
||||
{
|
||||
variable *var = lookup_existing_symbol(var_name);
|
||||
if (!var) {
|
||||
return var;
|
||||
}
|
||||
if (var->type != sd_set) {
|
||||
PERROR("ASSERT: delete_set_var: deleting %s but is a boolean variable\n",
|
||||
var_name);
|
||||
exit(1);
|
||||
}
|
||||
variable *save = new variable(*var);
|
||||
my_symtab.erase(var->var_name);
|
||||
return save;
|
||||
}
|
||||
|
||||
void symtab::free_symtab()
|
||||
{
|
||||
my_symtab.erase(my_symtab.begin(), my_symtab.end());
|
||||
}
|
||||
|
||||
void symtab::dump(bool do_expanded)
|
||||
{
|
||||
for (auto var : my_symtab) {
|
||||
var.second.dump(do_expanded);
|
||||
}
|
||||
}
|
||||
|
||||
void symtab::expand_variables()
|
||||
{
|
||||
for (auto var : my_symtab) {
|
||||
if (var.second.type != sd_boolean)
|
||||
var.second.expand_variable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
variable *symtab::get_set_var(const char *name)
|
||||
{
|
||||
char *var_name = variable::process_var(name);
|
||||
variable *var = lookup_existing_symbol(var_name);
|
||||
if (!var) {
|
||||
return var;
|
||||
}
|
||||
if (var->type != sd_set) {
|
||||
PERROR("Variable %s is not a set variable\n", var_name);
|
||||
return nullptr;
|
||||
}
|
||||
var->expand_variable();
|
||||
return var;
|
||||
}
|
||||
|
||||
variable *symtab::get_boolean_var(const char *name)
|
||||
{
|
||||
char *var_name = variable::process_var(name);
|
||||
variable *var = lookup_existing_symbol(var_name);
|
||||
if (!var) {
|
||||
return var;
|
||||
}
|
||||
if (var->type != sd_boolean) {
|
||||
PERROR("Variable %s is not a boolean variable\n", var_name);
|
||||
return nullptr;
|
||||
}
|
||||
free(var_name);
|
||||
return var;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, variable> symtab::my_symtab;
|
47
parser/symtab.h
Normal file
47
parser/symtab.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2025
|
||||
* Canonical Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_SYMTAB_H
|
||||
#define __AA_SYMTAB_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#include "variable.h"
|
||||
#include "parser.h"
|
||||
|
||||
class symtab {
|
||||
private:
|
||||
static std::unordered_map<std::string, variable> my_symtab;
|
||||
public:
|
||||
template <typename T>
|
||||
static int add_var(const char *var, T value);
|
||||
static int add_var(const char *var, int value);
|
||||
static int add_var(const char *var, struct value_list *value);
|
||||
static int add_var(const char *var, const char *value);
|
||||
static int add_var(variable var);
|
||||
static int add_set_value(const char *var, struct value_list *value);
|
||||
static void dump(bool do_expanded);
|
||||
static void free_symtab(void);
|
||||
static void expand_variables(void);
|
||||
static variable *lookup_existing_symbol(const char *var_name);
|
||||
static variable *get_set_var(const char *var_name);
|
||||
static variable *get_boolean_var(const char *var_name);
|
||||
static variable *delete_var(const char *var_name);
|
||||
};
|
||||
|
||||
#endif /* __AA_SYMTAB_H */
|
355
parser/variable.cc
Normal file
355
parser/variable.cc
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2025
|
||||
* Canonical Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
|
||||
#include "variable.h"
|
||||
#include "symtab.h"
|
||||
|
||||
variable::variable(const char *var_name, struct value_list *values):
|
||||
type(sd_set),
|
||||
var_name(var_name)
|
||||
{
|
||||
struct value_list *entry = NULL;
|
||||
if (!values || !values->value) {
|
||||
yyerror("Assert: valuelist returned NULL");
|
||||
}
|
||||
PDEBUG("Matched: set assignment for (%s)\n", var_name);
|
||||
|
||||
list_for_each(values, entry) {
|
||||
this->values.insert(entry->value);
|
||||
}
|
||||
}
|
||||
|
||||
variable::variable(const char *var_name, const char *value):
|
||||
type(sd_set),
|
||||
var_name(var_name)
|
||||
{
|
||||
PDEBUG("Matched: set assignment for (%s)\n", var_name);
|
||||
this->values.insert(value);
|
||||
}
|
||||
|
||||
variable::variable(const char *var_name, int boolean):
|
||||
type(sd_boolean),
|
||||
var_name(var_name),
|
||||
boolean(boolean)
|
||||
{
|
||||
PDEBUG("Matched: boolean assignment (%s) to %d\n", var_name, boolean);
|
||||
}
|
||||
|
||||
char *variable::process_var(const char *var)
|
||||
{
|
||||
const char *orig = var;
|
||||
const char *valid;
|
||||
int len = strlen(var);
|
||||
int i;
|
||||
|
||||
if (*orig == '@' || *orig == '$') {
|
||||
orig++;
|
||||
len--;
|
||||
} else {
|
||||
PERROR("ASSERT: Found var '%s' without variable prefix\n",
|
||||
var);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*orig == '{') {
|
||||
orig++;
|
||||
len--;
|
||||
if (orig[len - 1] != '}') {
|
||||
PERROR("ASSERT: No matching '}' in variable '%s'\n",
|
||||
var);
|
||||
return NULL;
|
||||
} else
|
||||
len--;
|
||||
}
|
||||
|
||||
valid = orig;
|
||||
for (i = 0; i < len; i++) {
|
||||
/* first character must be alpha */
|
||||
if (valid[i] == *orig) {
|
||||
if (!isalpha(valid[i])) {
|
||||
PERROR("Variable '%s' must start with alphabet letters\n",
|
||||
var);
|
||||
return NULL;
|
||||
}
|
||||
} else if (!(valid[i] == '_' || isalnum(valid[i]))) {
|
||||
PERROR("Variable '%s' contains invalid characters\n",
|
||||
var);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return strndup(orig, len);
|
||||
}
|
||||
|
||||
int variable::add_set_value(struct value_list *values)
|
||||
{
|
||||
struct value_list *entry = NULL;
|
||||
PDEBUG("Matched: additive assignment for (%s)\n", var_name.c_str());
|
||||
|
||||
if (type != sd_set) {
|
||||
PERROR("Variable %s is not a set variable\n", var_name.c_str());
|
||||
return 2;
|
||||
}
|
||||
|
||||
list_for_each(values, entry) {
|
||||
this->values.insert(entry->value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string, std::string> extract_variable(const std::string& input)
|
||||
{
|
||||
std::string prefix = "";
|
||||
std::string variable = "";
|
||||
std::string suffix = "";
|
||||
|
||||
ssize_t start_pattern_pos = -1;
|
||||
ssize_t end_pattern_pos = -1;
|
||||
size_t var_len;
|
||||
|
||||
bool bEscape = false;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < input.size(); i++) {
|
||||
switch(input[i]) {
|
||||
case '\\':
|
||||
bEscape = !bEscape;
|
||||
break;
|
||||
case '@':
|
||||
if (bEscape) {
|
||||
bEscape = false;
|
||||
} else if (input[i + 1] == '{') {
|
||||
start_pattern_pos = i;
|
||||
i += 2;
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
if (bEscape) {
|
||||
bEscape = false;
|
||||
} else if (start_pattern_pos != -1) {
|
||||
end_pattern_pos = i;
|
||||
goto found;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bEscape = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found:
|
||||
if (start_pattern_pos == -1 || end_pattern_pos == -1) {
|
||||
return std::make_tuple(prefix, variable, suffix); // "@{" or '}' not found
|
||||
}
|
||||
|
||||
var_len = end_pattern_pos - start_pattern_pos + 1;
|
||||
|
||||
prefix = input.substr(0, start_pattern_pos);
|
||||
variable = input.substr(start_pattern_pos, var_len);
|
||||
suffix = input.substr(end_pattern_pos + 1);
|
||||
|
||||
return std::make_tuple(prefix, variable, suffix);
|
||||
}
|
||||
|
||||
static void trim_leading_slash(std::string& str)
|
||||
{
|
||||
std::size_t found = str.find_first_not_of('/');
|
||||
if (found != std::string::npos)
|
||||
str.erase(0, found);
|
||||
else
|
||||
str.clear(); // str is all '/'
|
||||
}
|
||||
|
||||
static void trim_trailing_slash(std::string& str)
|
||||
{
|
||||
std::size_t found = str.find_last_not_of('/');
|
||||
if (found != std::string::npos)
|
||||
str.erase(found + 1);
|
||||
else
|
||||
str.clear(); // str is all '/'
|
||||
}
|
||||
|
||||
int variable::expand_by_alternation(char **name)
|
||||
{
|
||||
std::string expanded_name = "";
|
||||
bool filter_leading_slash = false;
|
||||
bool filter_trailing_slash = false;
|
||||
|
||||
if (!name) {
|
||||
PERROR("ASSERT: name to be expanded cannot be NULL\n");
|
||||
exit(1);
|
||||
}
|
||||
if (!*name) /* can happen when entry is optional */
|
||||
return 0;
|
||||
|
||||
auto result = extract_variable(*name);
|
||||
std::string prefix = std::get<0>(result);
|
||||
std::string var = std::get<1>(result);
|
||||
std::string suffix = std::get<2>(result);
|
||||
|
||||
if (prefix.empty() && var.empty() && suffix.empty()) {
|
||||
return 0; /* no var found, name is unchanged */
|
||||
}
|
||||
|
||||
free(*name);
|
||||
|
||||
if (!prefix.empty() && prefix[prefix.size() - 1] == '/') {
|
||||
/* make sure to not collapse / in the beginning of the path */
|
||||
std::size_t found = prefix.find_first_not_of('/');
|
||||
if (found != std::string::npos)
|
||||
filter_leading_slash = true;
|
||||
}
|
||||
if (!suffix.empty() && suffix[0] == '/')
|
||||
filter_trailing_slash = true;
|
||||
|
||||
variable *ref = symtab::get_set_var(var.c_str());
|
||||
if (!ref) {
|
||||
PERROR("Failed to find declaration for: %s\n", var.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t setsize = ref->expanded.size();
|
||||
auto i = ref->expanded.begin();
|
||||
|
||||
if (setsize > 1) {
|
||||
expanded_name += "{";
|
||||
}
|
||||
|
||||
do {
|
||||
std::string s = *i;
|
||||
if (filter_leading_slash)
|
||||
trim_leading_slash(s);
|
||||
if (filter_trailing_slash)
|
||||
trim_trailing_slash(s);
|
||||
|
||||
if (i != ref->expanded.begin()) {
|
||||
expanded_name += ",";
|
||||
}
|
||||
|
||||
expanded_name += s;
|
||||
} while (++i != ref->expanded.end());
|
||||
|
||||
if (setsize > 1) {
|
||||
expanded_name += "}";
|
||||
}
|
||||
|
||||
expanded_name = prefix + expanded_name + suffix;
|
||||
*name = strdup(expanded_name.c_str());
|
||||
if (!*name) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
/* recursive until no variables are found in *name */
|
||||
return expand_by_alternation(name);
|
||||
}
|
||||
|
||||
int variable::expand_variable()
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (type == sd_boolean) {
|
||||
PERROR("Referenced variable %s is a boolean used in set context\n",
|
||||
var_name.c_str());
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* already done */
|
||||
if (!expanded.empty())
|
||||
return 0;
|
||||
|
||||
expanding = true;
|
||||
|
||||
std::list<std::string> work_set(values.begin(), values.end());
|
||||
for (auto value : work_set) {
|
||||
auto result = extract_variable(value);
|
||||
std::string prefix = std::get<0>(result);
|
||||
std::string var = std::get<1>(result);
|
||||
std::string suffix = std::get<2>(result);
|
||||
|
||||
if (prefix.empty() && var.empty() && suffix.empty()) {
|
||||
expanded.insert(value); /* no var left to expand */
|
||||
continue;
|
||||
}
|
||||
char *name = variable::process_var(var.c_str());
|
||||
variable *ref = symtab::lookup_existing_symbol(name);
|
||||
if (!ref) {
|
||||
PERROR("Failed to find declaration for: %s\n", var.c_str());
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
if (ref->expanding) {
|
||||
PERROR("Variable @{%s} is referenced recursively (by @{%s})\n",
|
||||
ref->var_name.c_str(), var_name.c_str());
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
rc = ref->expand_variable();
|
||||
if (rc != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ref->expanded.empty()) {
|
||||
PERROR("ASSERT: Variable @{%s} should have been expanded but isn't\n",
|
||||
ref->var_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
for (auto refvalue : ref->expanded) {
|
||||
/* there could still be vars in suffix, so add
|
||||
* to work_set, not expanded */
|
||||
work_set.push_back(prefix + refvalue + suffix);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
expanding = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void variable::dump_set_values(std::set<std::string> values)
|
||||
{
|
||||
for (auto value : values)
|
||||
printf(" \"%s\"", value.c_str());
|
||||
}
|
||||
|
||||
void variable::dump(bool do_expanded)
|
||||
{
|
||||
switch(type) {
|
||||
case sd_boolean:
|
||||
printf("$%s = %s\n", var_name.c_str(),
|
||||
boolean ? "true" : "false");
|
||||
break;
|
||||
case sd_set:
|
||||
printf("@%s =", var_name.c_str());
|
||||
if (do_expanded) {
|
||||
if (expanded.empty()) {
|
||||
expand_variable();
|
||||
}
|
||||
dump_set_values(expanded);
|
||||
} else {
|
||||
dump_set_values(values);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
PERROR("ASSERT: unknown symbol table type for %s\n", var_name.c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
58
parser/variable.h
Normal file
58
parser/variable.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2025
|
||||
* Canonical Ltd. (All rights reserved)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Canonical Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_VARIABLE_H
|
||||
#define __AA_VARIABLE_H
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "parser.h"
|
||||
|
||||
enum var_type {
|
||||
sd_boolean,
|
||||
sd_set,
|
||||
};
|
||||
|
||||
class variable {
|
||||
public:
|
||||
enum var_type type;
|
||||
std::string var_name;
|
||||
int boolean;
|
||||
std::set<std::string> values;
|
||||
std::set<std::string> expanded;
|
||||
std::string expanding_by = "";
|
||||
bool expanding = false;
|
||||
|
||||
variable(const char *var_name, struct value_list *values);
|
||||
variable(const char *var_name, const char *value);
|
||||
variable(const char *var_name, int boolean);
|
||||
|
||||
int add_set_value(struct value_list *value);
|
||||
int expand_variable(void);
|
||||
static int expand_by_alternation(char **name);
|
||||
void dump_set_values(std::set<std::string> values);
|
||||
void dump(bool expanded);
|
||||
|
||||
virtual ~variable() {};
|
||||
|
||||
/* strip off surrounding delimiters around variables */
|
||||
static char *process_var(const char *var);
|
||||
};
|
||||
|
||||
std::tuple<std::string, std::string, std::string> extract_variable(const std::string& input);
|
||||
|
||||
#endif /* __AA_VARIABLE_H */
|
Loading…
x
Reference in New Issue
Block a user